Skip to content

Commit 20d369a

Browse files
bors[bot]Jonas Schievink
and
Jonas Schievink
authored
Merge #6299
6299: Diagnose items that are #[cfg]d out r=jonas-schievink a=jonas-schievink This emits a hint-level diagnostic with `Unnecessary` tag to "gray out" any items whose `#[cfg]` attributes remove the item before name resolution. Co-authored-by: Jonas Schievink <[email protected]>
2 parents 5dd99aa + 74ac83a commit 20d369a

File tree

8 files changed

+141
-67
lines changed

8 files changed

+141
-67
lines changed

crates/hir/src/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! FIXME: write short doc here
2-
pub use hir_def::diagnostics::UnresolvedModule;
2+
pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule};
33
pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder};
44
pub use hir_ty::diagnostics::{
55
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,

crates/hir_def/src/diagnostics.rs

+25
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,28 @@ impl Diagnostic for UnresolvedImport {
8686
true
8787
}
8888
}
89+
90+
// Diagnostic: unconfigured-code
91+
//
92+
// This diagnostic is shown for code with inactive `#[cfg]` attributes.
93+
#[derive(Debug)]
94+
pub struct InactiveCode {
95+
pub file: HirFileId,
96+
pub node: SyntaxNodePtr,
97+
}
98+
99+
impl Diagnostic for InactiveCode {
100+
fn code(&self) -> DiagnosticCode {
101+
DiagnosticCode("inactive-code")
102+
}
103+
fn message(&self) -> String {
104+
// FIXME: say *why* it is configured out
105+
"code is inactive due to #[cfg] directives".to_string()
106+
}
107+
fn display_source(&self) -> InFile<SyntaxNodePtr> {
108+
InFile::new(self.file, self.node.clone())
109+
}
110+
fn as_any(&self) -> &(dyn Any + Send + 'static) {
111+
self
112+
}
113+
}

crates/hir_def/src/item_tree.rs

+18
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,24 @@ impl ModItem {
672672
pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
673673
N::id_from_mod_item(self)
674674
}
675+
676+
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
677+
match self {
678+
ModItem::Import(it) => tree[it.index].ast_id().upcast(),
679+
ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(),
680+
ModItem::Function(it) => tree[it.index].ast_id().upcast(),
681+
ModItem::Struct(it) => tree[it.index].ast_id().upcast(),
682+
ModItem::Union(it) => tree[it.index].ast_id().upcast(),
683+
ModItem::Enum(it) => tree[it.index].ast_id().upcast(),
684+
ModItem::Const(it) => tree[it.index].ast_id().upcast(),
685+
ModItem::Static(it) => tree[it.index].ast_id().upcast(),
686+
ModItem::Trait(it) => tree[it.index].ast_id().upcast(),
687+
ModItem::Impl(it) => tree[it.index].ast_id().upcast(),
688+
ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(),
689+
ModItem::Mod(it) => tree[it.index].ast_id().upcast(),
690+
ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(),
691+
}
692+
}
675693
}
676694

677695
#[derive(Debug, Copy, Clone, Eq, PartialEq)]

crates/hir_def/src/nameres.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ mod diagnostics {
286286
use hir_expand::diagnostics::DiagnosticSink;
287287
use hir_expand::hygiene::Hygiene;
288288
use hir_expand::InFile;
289-
use syntax::{ast, AstPtr};
289+
use syntax::{ast, AstPtr, SyntaxNodePtr};
290290

291291
use crate::path::ModPath;
292292
use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
@@ -298,6 +298,8 @@ mod diagnostics {
298298
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
299299

300300
UnresolvedImport { ast: AstId<ast::Use>, index: usize },
301+
302+
UnconfiguredCode { ast: InFile<SyntaxNodePtr> },
301303
}
302304

303305
#[derive(Debug, PartialEq, Eq)]
@@ -336,6 +338,13 @@ mod diagnostics {
336338
Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } }
337339
}
338340

341+
pub(super) fn unconfigured_code(
342+
container: LocalModuleId,
343+
ast: InFile<SyntaxNodePtr>,
344+
) -> Self {
345+
Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast } }
346+
}
347+
339348
pub(super) fn add_to(
340349
&self,
341350
db: &dyn DefDatabase,
@@ -385,6 +394,10 @@ mod diagnostics {
385394
sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
386395
}
387396
}
397+
398+
DiagnosticKind::UnconfiguredCode { ast } => {
399+
sink.push(InactiveCode { file: ast.file_id, node: ast.value.clone() });
400+
}
388401
}
389402
}
390403
}

crates/hir_def/src/nameres/collector.rs

+13
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,7 @@ impl ModCollector<'_, '_> {
913913
for &item in items {
914914
let attrs = self.item_tree.attrs(item.into());
915915
if !self.is_cfg_enabled(attrs) {
916+
self.emit_unconfigured_diagnostic(item);
916917
continue;
917918
}
918919
let module =
@@ -1323,6 +1324,18 @@ impl ModCollector<'_, '_> {
13231324
fn is_cfg_enabled(&self, attrs: &Attrs) -> bool {
13241325
attrs.is_cfg_enabled(self.def_collector.cfg_options)
13251326
}
1327+
1328+
fn emit_unconfigured_diagnostic(&mut self, item: ModItem) {
1329+
let ast_id = item.ast_id(self.item_tree);
1330+
let id_map = self.def_collector.db.ast_id_map(self.file_id);
1331+
let syntax_ptr = id_map.get(ast_id).syntax_node_ptr();
1332+
1333+
let ast_node = InFile::new(self.file_id, syntax_ptr);
1334+
self.def_collector
1335+
.def_map
1336+
.diagnostics
1337+
.push(DefDiagnostic::unconfigured_code(self.module_id, ast_node));
1338+
}
13261339
}
13271340

13281341
fn is_macro_rules(path: &ModPath) -> bool {

crates/ide/src/diagnostics.rs

+50-39
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ mod field_shorthand;
1010
use std::cell::RefCell;
1111

1212
use base_db::SourceDatabase;
13-
use hir::{diagnostics::DiagnosticSinkBuilder, Semantics};
13+
use hir::{
14+
diagnostics::{Diagnostic as _, DiagnosticSinkBuilder},
15+
Semantics,
16+
};
1417
use ide_db::RootDatabase;
1518
use itertools::Itertools;
1619
use rustc_hash::FxHashSet;
@@ -31,6 +34,25 @@ pub struct Diagnostic {
3134
pub range: TextRange,
3235
pub severity: Severity,
3336
pub fix: Option<Fix>,
37+
pub unused: bool,
38+
}
39+
40+
impl Diagnostic {
41+
fn error(range: TextRange, message: String) -> Self {
42+
Self { message, range, severity: Severity::Error, fix: None, unused: false }
43+
}
44+
45+
fn hint(range: TextRange, message: String) -> Self {
46+
Self { message, range, severity: Severity::WeakWarning, fix: None, unused: false }
47+
}
48+
49+
fn with_fix(self, fix: Option<Fix>) -> Self {
50+
Self { fix, ..self }
51+
}
52+
53+
fn with_unused(self, unused: bool) -> Self {
54+
Self { unused, ..self }
55+
}
3456
}
3557

3658
#[derive(Debug)]
@@ -71,13 +93,13 @@ pub(crate) fn diagnostics(
7193
let mut res = Vec::new();
7294

7395
// [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
74-
res.extend(parse.errors().iter().take(128).map(|err| Diagnostic {
75-
// name: None,
76-
range: err.range(),
77-
message: format!("Syntax Error: {}", err),
78-
severity: Severity::Error,
79-
fix: None,
80-
}));
96+
res.extend(
97+
parse
98+
.errors()
99+
.iter()
100+
.take(128)
101+
.map(|err| Diagnostic::error(err.range(), format!("Syntax Error: {}", err))),
102+
);
81103

82104
for node in parse.tree().syntax().descendants() {
83105
check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
@@ -100,6 +122,13 @@ pub(crate) fn diagnostics(
100122
.on::<hir::diagnostics::IncorrectCase, _>(|d| {
101123
res.borrow_mut().push(warning_with_fix(d, &sema));
102124
})
125+
.on::<hir::diagnostics::InactiveCode, _>(|d| {
126+
// Override severity and mark as unused.
127+
res.borrow_mut().push(
128+
Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
129+
.with_unused(true),
130+
);
131+
})
103132
// Only collect experimental diagnostics when they're enabled.
104133
.filter(|diag| !(diag.is_experimental() && config.disable_experimental))
105134
.filter(|diag| !config.disabled.contains(diag.code().as_str()));
@@ -108,13 +137,8 @@ pub(crate) fn diagnostics(
108137
let mut sink = sink_builder
109138
// Diagnostics not handled above get no fix and default treatment.
110139
.build(|d| {
111-
res.borrow_mut().push(Diagnostic {
112-
// name: Some(d.name().into()),
113-
message: d.message(),
114-
range: sema.diagnostics_display_range(d).range,
115-
severity: Severity::Error,
116-
fix: None,
117-
})
140+
res.borrow_mut()
141+
.push(Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()));
118142
});
119143

120144
if let Some(m) = sema.to_module_def(file_id) {
@@ -125,22 +149,11 @@ pub(crate) fn diagnostics(
125149
}
126150

127151
fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
128-
Diagnostic {
129-
// name: Some(d.name().into()),
130-
range: sema.diagnostics_display_range(d).range,
131-
message: d.message(),
132-
severity: Severity::Error,
133-
fix: d.fix(&sema),
134-
}
152+
Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
135153
}
136154

137155
fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
138-
Diagnostic {
139-
range: sema.diagnostics_display_range(d).range,
140-
message: d.message(),
141-
severity: Severity::WeakWarning,
142-
fix: d.fix(&sema),
143-
}
156+
Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
144157
}
145158

146159
fn check_unnecessary_braces_in_use_statement(
@@ -161,17 +174,14 @@ fn check_unnecessary_braces_in_use_statement(
161174
edit_builder.finish()
162175
});
163176

164-
acc.push(Diagnostic {
165-
// name: None,
166-
range: use_range,
167-
message: "Unnecessary braces in use statement".to_string(),
168-
severity: Severity::WeakWarning,
169-
fix: Some(Fix::new(
170-
"Remove unnecessary braces",
171-
SourceFileEdit { file_id, edit }.into(),
172-
use_range,
173-
)),
174-
});
177+
acc.push(
178+
Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
179+
.with_fix(Some(Fix::new(
180+
"Remove unnecessary braces",
181+
SourceFileEdit { file_id, edit }.into(),
182+
use_range,
183+
))),
184+
);
175185
}
176186

177187
Some(())
@@ -578,6 +588,7 @@ fn test_fn() {
578588
fix_trigger_range: 0..8,
579589
},
580590
),
591+
unused: false,
581592
},
582593
]
583594
"#]],

crates/ide/src/diagnostics/field_shorthand.rs

+13-19
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use ide_db::source_change::SourceFileEdit;
66
use syntax::{ast, match_ast, AstNode, SyntaxNode};
77
use text_edit::TextEdit;
88

9-
use crate::{Diagnostic, Fix, Severity};
9+
use crate::{Diagnostic, Fix};
1010

1111
pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
1212
match_ast! {
@@ -46,17 +46,15 @@ fn check_expr_field_shorthand(
4646
let edit = edit_builder.finish();
4747

4848
let field_range = record_field.syntax().text_range();
49-
acc.push(Diagnostic {
50-
// name: None,
51-
range: field_range,
52-
message: "Shorthand struct initialization".to_string(),
53-
severity: Severity::WeakWarning,
54-
fix: Some(Fix::new(
55-
"Use struct shorthand initialization",
56-
SourceFileEdit { file_id, edit }.into(),
57-
field_range,
58-
)),
59-
});
49+
acc.push(
50+
Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix(
51+
Some(Fix::new(
52+
"Use struct shorthand initialization",
53+
SourceFileEdit { file_id, edit }.into(),
54+
field_range,
55+
)),
56+
),
57+
);
6058
}
6159
}
6260

@@ -88,17 +86,13 @@ fn check_pat_field_shorthand(
8886
let edit = edit_builder.finish();
8987

9088
let field_range = record_pat_field.syntax().text_range();
91-
acc.push(Diagnostic {
92-
// name: None,
93-
range: field_range,
94-
message: "Shorthand struct pattern".to_string(),
95-
severity: Severity::WeakWarning,
96-
fix: Some(Fix::new(
89+
acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix(
90+
Some(Fix::new(
9791
"Use struct field shorthand",
9892
SourceFileEdit { file_id, edit }.into(),
9993
field_range,
10094
)),
101-
});
95+
));
10296
}
10397
}
10498

crates/rust-analyzer/src/handlers.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ use lsp_server::ErrorCode;
1616
use lsp_types::{
1717
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
1818
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
19-
CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams,
20-
DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location,
21-
Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
22-
SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
23-
SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
24-
TextDocumentIdentifier, Url, WorkspaceEdit,
19+
CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag,
20+
DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
21+
HoverContents, Location, Position, PrepareRenameResponse, Range, RenameParams,
22+
SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
23+
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
24+
SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
2525
};
2626
use project_model::TargetKind;
2727
use serde::{Deserialize, Serialize};
@@ -1124,7 +1124,7 @@ pub(crate) fn publish_diagnostics(
11241124
source: Some("rust-analyzer".to_string()),
11251125
message: d.message,
11261126
related_information: None,
1127-
tags: None,
1127+
tags: if d.unused { Some(vec![DiagnosticTag::Unnecessary]) } else { None },
11281128
})
11291129
.collect();
11301130
Ok(diagnostics)

0 commit comments

Comments
 (0)