Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ide-diagnostics/Cargo.toml1
-rw-r--r--crates/ide-diagnostics/src/handlers/inactive_code.rs89
-rw-r--r--crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/no_such_field.rs8
-rw-r--r--crates/ide-diagnostics/src/lib.rs54
-rw-r--r--crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs1
7 files changed, 134 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e02b9e157b..a096be2f2a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1156,6 +1156,7 @@ dependencies = [
"itertools 0.15.0",
"paths",
"serde_json",
+ "smallvec",
"stdx",
"syntax",
"test-fixture",
diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml
index ddf5999036..ce83619757 100644
--- a/crates/ide-diagnostics/Cargo.toml
+++ b/crates/ide-diagnostics/Cargo.toml
@@ -18,6 +18,7 @@ either.workspace = true
itertools.workspace = true
serde_json.workspace = true
tracing.workspace = true
+smallvec.workspace = true
# local deps
stdx.workspace = true
diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 09f3e8bfb3..9e38d8f1b9 100644
--- a/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -6,14 +6,14 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
// Diagnostic: inactive-code
//
// This diagnostic is shown for code with inactive `#[cfg]` attributes.
+//
+// It can be disabled selectively with `#[allow(rust_analyzer::inactive_code)]`.
pub(crate) fn inactive_code(
ctx: &DiagnosticsContext<'_, '_>,
d: &hir::InactiveCode,
) -> Option<Diagnostic> {
- // If there's inactive code somewhere in a macro, don't propagate to the call-site.
- if d.node.file_id.is_macro() {
- return None;
- }
+ // If there's inactive code somewhere in a macro that doesn't map to something in the call, don't propagate to the call-site.
+ d.node.map(|it| it.text_range()).original_node_file_range_rooted_opt(ctx.db())?;
let inactive = DnfExpr::new(&d.cfg).why_inactive(&d.opts);
let mut message = "code is inactive due to #[cfg] directives".to_owned();
@@ -28,10 +28,11 @@ pub(crate) fn inactive_code(
}
}
// FIXME: This shouldn't be a diagnostic
- let res = Diagnostic::new(
- DiagnosticCode::Ra("inactive-code", Severity::WeakWarning),
+ let res = Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RaLint("inactive_code", Severity::WeakWarning),
message,
- ctx.sema.diagnostics_display_range(d.node),
+ d.node,
)
.stable()
.with_unused(true);
@@ -239,7 +240,7 @@ fn foo() {}
};
assert_eq!(
inactive_code.code,
- DiagnosticCode::Ra("inactive-code", ide_db::Severity::WeakWarning)
+ DiagnosticCode::RaLint("inactive_code", ide_db::Severity::WeakWarning)
);
assert_eq!(
inactive_code.message,
@@ -252,4 +253,76 @@ fn foo() {}
ide_db::FileRange { file_id: file_id.file_id(&db), range: full_file_range },
);
}
+
+ #[test]
+ fn cfg_in_macro_does_not_diagnose_the_whole_call() {
+ check(
+ r#"
+macro_rules! m {
+ ($e:item) => {
+ #[cfg(false)]
+ const _: () = ();
+
+ $e
+ };
+}
+
+m! {
+ fn foo() {}
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn in_macro() {
+ check(
+ r#"
+macro_rules! m {
+ ($e:item) => {
+ $e
+ };
+}
+
+m! {
+ #[cfg(false)] fn foo() {}
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: false is disabled
+}
+ "#,
+ );
+ check(
+ r#"
+macro_rules! m {
+ ($e:item) => {
+ #[cfg(false)]
+ $e
+ };
+}
+
+m! {
+ fn foo() {}
+ // ^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: false is disabled
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn allow() {
+ check(
+ r#"
+macro_rules! m {
+ ($e:item) => {
+ #[cfg(false)]
+ #[allow(rust_analyzer::inactive_code)]
+ $e
+ };
+}
+
+m! {
+ fn foo() {}
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 81ea442e9e..844431c1e5 100644
--- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -399,6 +399,8 @@ fn main() {
fn cfgd_out_call_arguments() {
check_diagnostics(
r#"
+#![allow(rust_analyzer::inactive_code)]
+
struct C(#[cfg(FALSE)] ());
impl C {
fn new() -> Self {
@@ -422,6 +424,8 @@ fn main() {
fn cfgd_out_fn_params() {
check_diagnostics(
r#"
+#![allow(rust_analyzer::inactive_code)]
+
fn foo(#[cfg(NEVER)] x: ()) {}
struct S;
diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs
index cc900808b5..d61c524341 100644
--- a/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -156,6 +156,8 @@ mod tests {
fn dont_work_for_field_with_disabled_cfg() {
check_diagnostics(
r#"
+#![allow(rust_analyzer::inactive_code)]
+
struct Test {
#[cfg(feature = "hello")]
test: u32,
@@ -227,6 +229,8 @@ impl S {
check_diagnostics(
r#"
//- /lib.rs crate:foo cfg:feature=foo
+#![allow(rust_analyzer::inactive_code)]
+
struct MyStruct {
my_val: usize,
#[cfg(feature = "foo")]
@@ -252,6 +256,8 @@ impl MyStruct {
check_diagnostics(
r#"
//- /lib.rs crate:foo cfg:feature=foo
+#![allow(rust_analyzer::inactive_code)]
+
enum Foo {
#[cfg(not(feature = "foo"))]
Buz,
@@ -275,6 +281,8 @@ fn test_fn(f: Foo) {
check_diagnostics(
r#"
//- /lib.rs crate:foo cfg:feature=foo
+#![allow(rust_analyzer::inactive_code)]
+
struct S {
#[cfg(feature = "foo")]
foo: u32,
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 819b644dfa..26e4a84d2e 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -129,9 +129,11 @@ use ide_db::{
rename::RenameConfig,
source_change::SourceChange,
};
+use smallvec::{SmallVec, smallvec};
use syntax::{
AstPtr, Edition, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange,
ast::{self, AstNode},
+ format_smolstr,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
@@ -141,6 +143,7 @@ pub enum DiagnosticCode {
RustcLint(&'static str),
Clippy(&'static str),
Ra(&'static str, Severity),
+ RaLint(&'static str, Severity),
}
impl DiagnosticCode {
@@ -158,7 +161,7 @@ impl DiagnosticCode {
DiagnosticCode::Clippy(e) => {
format!("https://rust-lang.github.io/rust-clippy/master/#/{e}")
}
- DiagnosticCode::Ra(e, _) => {
+ DiagnosticCode::Ra(e, _) | DiagnosticCode::RaLint(e, _) => {
format!("https://rust-analyzer.github.io/book/diagnostics.html#{e}")
}
}
@@ -169,7 +172,8 @@ impl DiagnosticCode {
DiagnosticCode::RustcHardError(r)
| DiagnosticCode::RustcLint(r)
| DiagnosticCode::Clippy(r)
- | DiagnosticCode::Ra(r, _) => r,
+ | DiagnosticCode::Ra(r, _)
+ | DiagnosticCode::RaLint(r, _) => r,
DiagnosticCode::SyntaxError => "syntax-error",
}
}
@@ -206,7 +210,7 @@ impl Diagnostic {
// FIXME: We can make this configurable, and if the user uses `cargo clippy` on flycheck, we can
// make it normal warning.
DiagnosticCode::Clippy(_) => Severity::WeakWarning,
- DiagnosticCode::Ra(_, s) => s,
+ DiagnosticCode::Ra(_, s) | DiagnosticCode::RaLint(_, s) => s,
},
unused: false,
experimental: true,
@@ -573,7 +577,14 @@ pub fn semantic_diagnostics(
let mut lints = res
.iter_mut()
- .filter(|it| matches!(it.code, DiagnosticCode::Clippy(_) | DiagnosticCode::RustcLint(_)))
+ .filter(|it| {
+ matches!(
+ it.code,
+ DiagnosticCode::Clippy(_)
+ | DiagnosticCode::RustcLint(_)
+ | DiagnosticCode::RaLint(..)
+ )
+ })
.filter_map(|it| Some((it.main_node(&ctx.sema)?, it)))
.collect::<Vec<_>>();
@@ -646,7 +657,7 @@ fn handle_diag_from_macros(
struct BuiltLint {
lint: &'static Lint,
- groups: Vec<&'static str>,
+ groups: SmallVec<[SmolStr; 5]>,
}
static RUSTC_LINTS: LazyLock<FxHashMap<&str, BuiltLint>> =
@@ -667,12 +678,17 @@ fn build_lints_map(
) -> FxHashMap<&'static str, BuiltLint> {
let mut map_with_prefixes: FxHashMap<_, _> = lints
.iter()
- .map(|lint| (lint.label, BuiltLint { lint, groups: vec![lint.label, "__RA_EVERY_LINT"] }))
+ .map(|lint| {
+ (
+ lint.label,
+ BuiltLint { lint, groups: smallvec![lint.label.into(), "__RA_EVERY_LINT".into()] },
+ )
+ })
.collect();
for g in lint_group {
let mut add_children = |label: &'static str| {
for child in g.children {
- map_with_prefixes.get_mut(child).unwrap().groups.push(label);
+ map_with_prefixes.get_mut(child).unwrap().groups.push(label.into());
}
};
add_children(g.lint.label);
@@ -693,12 +709,15 @@ fn handle_lints(
edition: Edition,
) {
for (node, diag) in diagnostics {
- let lint = match diag.code {
- DiagnosticCode::RustcLint(lint) => RUSTC_LINTS[lint].lint,
- DiagnosticCode::Clippy(lint) => CLIPPY_LINTS[lint].lint,
- _ => panic!("non-lint passed to `handle_lints()`"),
+ let default_severity = 'find_severity: {
+ let lint = match diag.code {
+ DiagnosticCode::RustcLint(lint) => RUSTC_LINTS[lint].lint,
+ DiagnosticCode::Clippy(lint) => CLIPPY_LINTS[lint].lint,
+ DiagnosticCode::RaLint(_, severity) => break 'find_severity severity,
+ _ => panic!("non-lint passed to `handle_lints()`"),
+ };
+ default_lint_severity(lint, edition)
};
- let default_severity = default_lint_severity(lint, edition);
if !(default_severity == Severity::Allow && diag.severity == Severity::WeakWarning) {
diag.severity = default_severity;
}
@@ -798,13 +817,13 @@ fn lint_attrs(
#[derive(Debug)]
struct LintGroups {
- groups: &'static [&'static str],
+ groups: SmallVec<[SmolStr; 5]>,
inside_warnings: bool,
}
impl LintGroups {
fn contains(&self, group: &str) -> bool {
- self.groups.contains(&group) || (self.inside_warnings && group == "warnings")
+ self.groups.iter().any(|g| g == group) || (self.inside_warnings && group == "warnings")
}
}
@@ -813,12 +832,15 @@ fn lint_groups(lint: &DiagnosticCode, edition: Edition) -> LintGroups {
DiagnosticCode::RustcLint(name) => {
let lint = &RUSTC_LINTS[name];
let inside_warnings = default_lint_severity(lint.lint, edition) == Severity::Warning;
- (&lint.groups, inside_warnings)
+ (lint.groups.clone(), inside_warnings)
}
DiagnosticCode::Clippy(name) => {
let lint = &CLIPPY_LINTS[name];
let inside_warnings = default_lint_severity(lint.lint, edition) == Severity::Warning;
- (&lint.groups, inside_warnings)
+ (lint.groups.clone(), inside_warnings)
+ }
+ DiagnosticCode::RaLint(name, severity) => {
+ (smallvec![format_smolstr!("rust_analyzer::{name}")], *severity == Severity::Warning)
}
_ => panic!("non-lint passed to `handle_lints()`"),
};
diff --git a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
index 301613e920..34cf80a85b 100644
--- a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
+++ b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
@@ -2729,6 +2729,7 @@ tracing::error!();
"unresolved-macro-call",
"syntax-error",
"macro-error",
+ "inactive_code",
],
);
}