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. pub(crate) fn inactive_code( ctx: &DiagnosticsContext<'_>, d: &hir::InactiveCode, ) -> Option { // 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; } 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( DiagnosticCode::Ra("inactive-code", Severity::WeakWarning), message, ctx.sema.diagnostics_display_range(d.node), ) .stable() .with_unused(true); Some(res) } #[cfg(test)] mod tests { use crate::{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 "#, ); } }