Skip to content

Commit f85e383

Browse files
committed
internal: refactor inactive code diagnostics
1 parent fa9ed4e commit f85e383

File tree

6 files changed

+141
-133
lines changed

6 files changed

+141
-133
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
//! be expressed in terms of hir types themselves.
66
use std::any::Any;
77

8-
use cfg::{CfgExpr, CfgOptions, DnfExpr};
8+
use cfg::{CfgExpr, CfgOptions};
99
use either::Either;
1010
use hir_def::path::ModPath;
1111
use hir_expand::{name::Name, HirFileId, InFile};
12-
use stdx::format_to;
1312
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
1413

1514
pub use crate::diagnostics_sink::{
@@ -38,6 +37,7 @@ diagnostics![
3837
UnresolvedImport,
3938
UnresolvedMacroCall,
4039
MissingFields,
40+
InactiveCode,
4141
];
4242

4343
#[derive(Debug)]
@@ -62,39 +62,13 @@ pub struct UnresolvedMacroCall {
6262
pub path: ModPath,
6363
}
6464

65-
// Diagnostic: inactive-code
66-
//
67-
// This diagnostic is shown for code with inactive `#[cfg]` attributes.
6865
#[derive(Debug, Clone, Eq, PartialEq)]
6966
pub struct InactiveCode {
70-
pub file: HirFileId,
71-
pub node: SyntaxNodePtr,
67+
pub node: InFile<SyntaxNodePtr>,
7268
pub cfg: CfgExpr,
7369
pub opts: CfgOptions,
7470
}
7571

76-
impl Diagnostic for InactiveCode {
77-
fn code(&self) -> DiagnosticCode {
78-
DiagnosticCode("inactive-code")
79-
}
80-
fn message(&self) -> String {
81-
let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
82-
let mut buf = "code is inactive due to #[cfg] directives".to_string();
83-
84-
if let Some(inactive) = inactive {
85-
format_to!(buf, ": {}", inactive);
86-
}
87-
88-
buf
89-
}
90-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
91-
InFile::new(self.file, self.node.clone())
92-
}
93-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
94-
self
95-
}
96-
}
97-
9872
// Diagnostic: unresolved-proc-macro
9973
//
10074
// This diagnostic is shown when a procedural macro can not be found. This usually means that

crates/hir/src/lib.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -506,12 +506,14 @@ impl Module {
506506

507507
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
508508
let item = ast.to_node(db.upcast());
509-
sink.push(InactiveCode {
510-
file: ast.file_id,
511-
node: AstPtr::new(&item).into(),
512-
cfg: cfg.clone(),
513-
opts: opts.clone(),
514-
});
509+
acc.push(
510+
InactiveCode {
511+
node: ast.with_value(AstPtr::new(&item).into()),
512+
cfg: cfg.clone(),
513+
opts: opts.clone(),
514+
}
515+
.into(),
516+
);
515517
}
516518

517519
DefDiagnosticKind::UnresolvedProcMacro { ast } => {
@@ -1045,12 +1047,10 @@ impl Function {
10451047
let source_map = db.body_with_source_map(self.id.into()).1;
10461048
for diag in source_map.diagnostics() {
10471049
match diag {
1048-
BodyDiagnostic::InactiveCode { node, cfg, opts } => sink.push(InactiveCode {
1049-
file: node.file_id,
1050-
node: node.value.clone(),
1051-
cfg: cfg.clone(),
1052-
opts: opts.clone(),
1053-
}),
1050+
BodyDiagnostic::InactiveCode { node, cfg, opts } => acc.push(
1051+
InactiveCode { node: node.clone(), cfg: cfg.clone(), opts: opts.clone() }
1052+
.into(),
1053+
),
10541054
BodyDiagnostic::MacroError { node, message } => sink.push(MacroError {
10551055
file: node.file_id,
10561056
node: node.value.clone().into(),

crates/hir_def/src/body/tests.rs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -88,39 +88,6 @@ mod m {
8888
);
8989
}
9090

91-
#[test]
92-
fn cfg_diagnostics() {
93-
check_diagnostics(
94-
r"
95-
fn f() {
96-
// The three g̶e̶n̶d̶e̶r̶s̶ statements:
97-
98-
#[cfg(a)] fn f() {} // Item statement
99-
//^^^^^^^^^^^^^^^^^^^ InactiveCode
100-
#[cfg(a)] {} // Expression statement
101-
//^^^^^^^^^^^^ InactiveCode
102-
#[cfg(a)] let x = 0; // let statement
103-
//^^^^^^^^^^^^^^^^^^^^ InactiveCode
104-
105-
abc(#[cfg(a)] 0);
106-
//^^^^^^^^^^^ InactiveCode
107-
let x = Struct {
108-
#[cfg(a)] f: 0,
109-
//^^^^^^^^^^^^^^ InactiveCode
110-
};
111-
match () {
112-
() => (),
113-
#[cfg(a)] () => (),
114-
//^^^^^^^^^^^^^^^^^^ InactiveCode
115-
}
116-
117-
#[cfg(a)] 0 // Trailing expression of block
118-
//^^^^^^^^^^^ InactiveCode
119-
}
120-
",
121-
);
122-
}
123-
12491
#[test]
12592
fn macro_diag_builtin() {
12693
check_diagnostics(

crates/hir_def/src/nameres/tests/diagnostics.rs

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,48 +12,6 @@ fn check_no_diagnostics(ra_fixture: &str) {
1212
db.check_no_diagnostics();
1313
}
1414

15-
#[test]
16-
fn inactive_item() {
17-
// Additional tests in `cfg` crate. This only tests disabled cfgs.
18-
19-
check_diagnostics(
20-
r#"
21-
//- /lib.rs
22-
#[cfg(no)] pub fn f() {}
23-
//^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
24-
25-
#[cfg(no)] #[cfg(no2)] mod m;
26-
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
27-
28-
#[cfg(all(not(a), b))] enum E {}
29-
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
30-
31-
#[cfg(feature = "std")] use std;
32-
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
33-
"#,
34-
);
35-
}
36-
37-
/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
38-
#[test]
39-
fn inactive_via_cfg_attr() {
40-
cov_mark::check!(cfg_attr_active);
41-
check_diagnostics(
42-
r#"
43-
//- /lib.rs
44-
#[cfg_attr(not(never), cfg(no))] fn f() {}
45-
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
46-
47-
#[cfg_attr(not(never), cfg(not(no)))] fn f() {}
48-
49-
#[cfg_attr(never, cfg(no))] fn g() {}
50-
51-
#[cfg_attr(not(never), inline, cfg(no))] fn h() {}
52-
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
53-
"#,
54-
);
55-
}
56-
5715
#[test]
5816
fn builtin_macro_fails_expansion() {
5917
check_diagnostics(

crates/ide/src/diagnostics.rs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod unresolved_module;
88
mod unresolved_extern_crate;
99
mod unresolved_import;
1010
mod unresolved_macro_call;
11+
mod inactive_code;
1112
mod missing_fields;
1213

1314
mod fixes;
@@ -164,22 +165,6 @@ pub(crate) fn diagnostics(
164165
.on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| {
165166
res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
166167
})
167-
.on::<hir::diagnostics::InactiveCode, _>(|d| {
168-
// If there's inactive code somewhere in a macro, don't propagate to the call-site.
169-
if d.display_source().file_id.expansion_info(db).is_some() {
170-
return;
171-
}
172-
173-
// Override severity and mark as unused.
174-
res.borrow_mut().push(
175-
Diagnostic::hint(
176-
sema.diagnostics_display_range(d.display_source()).range,
177-
d.message(),
178-
)
179-
.with_unused(true)
180-
.with_code(Some(d.code())),
181-
);
182-
})
183168
.on::<UnlinkedFile, _>(|d| {
184169
// Limit diagnostic to the first few characters in the file. This matches how VS Code
185170
// renders it with the full span, but on other editors, and is less invasive.
@@ -247,6 +232,11 @@ pub(crate) fn diagnostics(
247232
AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d),
248233
AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d),
249234
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
235+
236+
AnyDiagnostic::InactiveCode(d) => match inactive_code::inactive_code(&ctx, &d) {
237+
Some(it) => it,
238+
None => continue,
239+
}
250240
};
251241
if let Some(code) = d.code {
252242
if ctx.config.disabled.contains(code.as_str()) {
@@ -451,7 +441,13 @@ mod tests {
451441
expect.assert_debug_eq(&diagnostics)
452442
}
453443

444+
#[track_caller]
454445
pub(crate) fn check_diagnostics(ra_fixture: &str) {
446+
check_diagnostics_with_inactive_code(ra_fixture, false)
447+
}
448+
449+
#[track_caller]
450+
pub(crate) fn check_diagnostics_with_inactive_code(ra_fixture: &str, with_inactive_code: bool) {
455451
let (analysis, file_id) = fixture::file(ra_fixture);
456452
let diagnostics = analysis
457453
.diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
@@ -460,7 +456,7 @@ mod tests {
460456
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
461457
let mut actual = diagnostics
462458
.into_iter()
463-
.filter(|d| d.code != Some(DiagnosticCode("inactive-code")))
459+
.filter(|d| d.code != Some(DiagnosticCode("inactive-code")) || with_inactive_code)
464460
.map(|d| (d.range, d.message))
465461
.collect::<Vec<_>>();
466462
actual.sort_by_key(|(range, _)| range.start());
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use cfg::DnfExpr;
2+
use stdx::format_to;
3+
4+
use crate::diagnostics::{Diagnostic, DiagnosticsContext};
5+
6+
// Diagnostic: inactive-code
7+
//
8+
// This diagnostic is shown for code with inactive `#[cfg]` attributes.
9+
pub(super) fn inactive_code(
10+
ctx: &DiagnosticsContext<'_>,
11+
d: &hir::InactiveCode,
12+
) -> Option<Diagnostic> {
13+
// If there's inactive code somewhere in a macro, don't propagate to the call-site.
14+
if d.node.file_id.expansion_info(ctx.sema.db).is_some() {
15+
return None;
16+
}
17+
18+
let inactive = DnfExpr::new(d.cfg.clone()).why_inactive(&d.opts);
19+
let mut message = "code is inactive due to #[cfg] directives".to_string();
20+
21+
if let Some(inactive) = inactive {
22+
format_to!(message, ": {}", inactive);
23+
}
24+
25+
let res = Diagnostic::new(
26+
"inactive-code",
27+
message,
28+
ctx.sema.diagnostics_display_range(d.node.clone()).range,
29+
)
30+
.with_unused(true);
31+
Some(res)
32+
}
33+
34+
#[cfg(test)]
35+
mod tests {
36+
use crate::diagnostics::tests::check_diagnostics_with_inactive_code;
37+
38+
#[test]
39+
fn cfg_diagnostics() {
40+
check_diagnostics_with_inactive_code(
41+
r#"
42+
fn f() {
43+
// The three g̶e̶n̶d̶e̶r̶s̶ statements:
44+
45+
#[cfg(a)] fn f() {} // Item statement
46+
//^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
47+
#[cfg(a)] {} // Expression statement
48+
//^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
49+
#[cfg(a)] let x = 0; // let statement
50+
//^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
51+
52+
abc(#[cfg(a)] 0);
53+
//^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
54+
let x = Struct {
55+
#[cfg(a)] f: 0,
56+
//^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
57+
};
58+
match () {
59+
() => (),
60+
#[cfg(a)] () => (),
61+
//^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
62+
}
63+
64+
#[cfg(a)] 0 // Trailing expression of block
65+
//^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
66+
}
67+
"#,
68+
true,
69+
);
70+
}
71+
72+
#[test]
73+
fn inactive_item() {
74+
// Additional tests in `cfg` crate. This only tests disabled cfgs.
75+
76+
check_diagnostics_with_inactive_code(
77+
r#"
78+
#[cfg(no)] pub fn f() {}
79+
//^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
80+
81+
#[cfg(no)] #[cfg(no2)] mod m;
82+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled
83+
84+
#[cfg(all(not(a), b))] enum E {}
85+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled
86+
87+
#[cfg(feature = "std")] use std;
88+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
89+
"#,
90+
true,
91+
);
92+
}
93+
94+
/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
95+
#[test]
96+
fn inactive_via_cfg_attr() {
97+
cov_mark::check!(cfg_attr_active);
98+
check_diagnostics_with_inactive_code(
99+
r#"
100+
#[cfg_attr(not(never), cfg(no))] fn f() {}
101+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
102+
103+
#[cfg_attr(not(never), cfg(not(no)))] fn f() {}
104+
105+
#[cfg_attr(never, cfg(no))] fn g() {}
106+
107+
#[cfg_attr(not(never), inline, cfg(no))] fn h() {}
108+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
109+
"#,
110+
true,
111+
);
112+
}
113+
}

0 commit comments

Comments
 (0)