| use cfg::DnfExpr; |
| use stdx::format_to; |
| |
| 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 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(); |
| |
| if let Some(inactive) = inactive { |
| let inactive_reasons = inactive.to_string(); |
| |
| if inactive_reasons.is_empty() { |
| format_to!(message); |
| } else { |
| format_to!(message, ": {}", inactive); |
| } |
| } |
| // FIXME: This shouldn't be a diagnostic |
| let res = Diagnostic::new_with_syntax_node_ptr( |
| ctx, |
| DiagnosticCode::RaLint("inactive_code", Severity::WeakWarning), |
| message, |
| d.node, |
| ) |
| .stable() |
| .with_unused(true); |
| Some(res) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use ide_db::RootDatabase; |
| use test_fixture::WithFixture; |
| |
| use crate::{DiagnosticCode, DiagnosticsConfig, tests::check_diagnostics_with_config}; |
| |
| #[track_caller] |
| pub(crate) fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { |
| let config = DiagnosticsConfig { |
| disabled: std::iter::once("unlinked-file".to_owned()).collect(), |
| ..DiagnosticsConfig::test_sample() |
| }; |
| check_diagnostics_with_config(config, ra_fixture) |
| } |
| |
| #[test] |
| fn cfg_diagnostics() { |
| check( |
| r#" |
| fn f() { |
| // The three g̶e̶n̶d̶e̶r̶s̶ statements: |
| |
| #[cfg(a)] fn f() {} // Item statement |
| //^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| #[cfg(a)] {} // Expression statement |
| //^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| #[cfg(a)] let x = 0; // let statement |
| //^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| |
| fn abc() {} |
| abc(#[cfg(a)] 0); |
| //^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| let x = Struct { |
| #[cfg(a)] f: 0, |
| //^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| }; |
| match () { |
| () => (), |
| #[cfg(a)] () => (), |
| //^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| } |
| |
| #[cfg(a)] 0 // Trailing expression of block |
| //^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn inactive_item() { |
| // Additional tests in `cfg` crate. This only tests disabled cfgs. |
| |
| check( |
| r#" |
| #[cfg(no)] pub fn f() {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled |
| |
| #[cfg(no)] #[cfg(no2)] mod m; |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled |
| |
| #[cfg(all(not(a), b))] enum E {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: b is disabled |
| |
| #[cfg(feature = "std")] use std; |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: feature = "std" is disabled |
| |
| #[cfg(any())] pub fn f() {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn inactive_assoc_item() { |
| check( |
| r#" |
| struct Foo; |
| impl Foo { |
| #[cfg(any())] pub fn f() {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives |
| } |
| |
| trait Bar { |
| #[cfg(any())] pub fn f() {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives |
| } |
| "#, |
| ); |
| } |
| |
| /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. |
| #[test] |
| fn inactive_via_cfg_attr() { |
| check( |
| r#" |
| #[cfg_attr(not(never), cfg(no))] fn f() {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled |
| |
| #[cfg_attr(not(never), cfg(not(no)))] fn f() {} |
| |
| #[cfg_attr(never, cfg(no))] fn g() {} |
| |
| #[cfg_attr(not(never), inline, cfg(no))] fn h() {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn inactive_fields_and_variants() { |
| check( |
| r#" |
| enum Foo { |
| #[cfg(a)] Bar, |
| //^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| Baz { |
| #[cfg(a)] baz: String, |
| //^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| }, |
| Qux(#[cfg(a)] String), |
| //^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| } |
| |
| struct Baz { |
| #[cfg(a)] baz: String, |
| //^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| } |
| |
| struct Qux(#[cfg(a)] String); |
| //^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| |
| union FooBar { |
| #[cfg(a)] baz: u32, |
| //^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn modules() { |
| check( |
| r#" |
| //- /main.rs |
| #[cfg(outline)] mod outline; |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: outline is disabled |
| |
| mod outline_inner; |
| //^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: outline_inner is disabled |
| |
| #[cfg(inline)] mod inline {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: inline is disabled |
| |
| //- /outline_inner.rs |
| #![cfg(outline_inner)] |
| //- /outline.rs |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn cfg_true_false() { |
| check( |
| r#" |
| #[cfg(false)] fn inactive() {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: false is disabled |
| |
| #[cfg(true)] fn active() {} |
| |
| #[cfg(any(not(true), false))] fn inactive2() {} |
| //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: true is enabled and false is disabled |
| |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn inactive_crate() { |
| let db = RootDatabase::with_files( |
| r#" |
| #![cfg(false)] |
| |
| fn foo() {} |
| "#, |
| ); |
| let file_id = db.test_crate().root_file_id(&db); |
| let diagnostics = hir::attach_db(&db, || { |
| crate::full_diagnostics( |
| &db, |
| &DiagnosticsConfig::test_sample(), |
| &ide_db::assists::AssistResolveStrategy::All, |
| file_id.file_id(&db), |
| ) |
| }); |
| let [inactive_code] = &*diagnostics else { |
| panic!("expected one inactive_code diagnostic, found {diagnostics:#?}"); |
| }; |
| assert_eq!( |
| inactive_code.code, |
| DiagnosticCode::RaLint("inactive_code", ide_db::Severity::WeakWarning) |
| ); |
| assert_eq!( |
| inactive_code.message, |
| "code is inactive due to #[cfg] directives: false is disabled", |
| ); |
| assert!(inactive_code.fixes.is_none()); |
| let full_file_range = file_id.parse(&db).syntax_node().text_range(); |
| assert_eq!( |
| inactive_code.range, |
| 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() {} |
| } |
| "#, |
| ); |
| } |
| } |