diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 1edb7c950e7b..4a3707dc9d16 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -20,28 +20,6 @@ use rustc_span::sym; use rustc_span::symbol::{Symbol, SymbolStr}; use semver::Version; -static UNIX_SYSTEMS: &[&str] = &[ - "android", - "dragonfly", - "emscripten", - "freebsd", - "fuchsia", - "haiku", - "illumos", - "ios", - "l4re", - "linux", - "macos", - "netbsd", - "openbsd", - "redox", - "solaris", - "vxworks", -]; - -// NOTE: windows is excluded from the list because it's also a valid target family. -static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"]; - declare_clippy_lint! { /// ### What it does /// Checks for items annotated with `#[inline(always)]`, @@ -221,39 +199,6 @@ declare_clippy_lint! { "usage of `cfg_attr(rustfmt)` instead of tool attributes" } -declare_clippy_lint! { - /// ### What it does - /// Checks for cfg attributes having operating systems used in target family position. - /// - /// ### Why is this bad? - /// The configuration option will not be recognised and the related item will not be included - /// by the conditional compilation engine. - /// - /// ### Example - /// Bad: - /// ```rust - /// #[cfg(linux)] - /// fn conditional() { } - /// ``` - /// - /// Good: - /// ```rust - /// #[cfg(target_os = "linux")] - /// fn conditional() { } - /// ``` - /// - /// Or: - /// ```rust - /// #[cfg(unix)] - /// fn conditional() { } - /// ``` - /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details. - #[clippy::version = "1.45.0"] - pub MISMATCHED_TARGET_OS, - correctness, - "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`" -} - declare_lint_pass!(Attributes => [ INLINE_ALWAYS, DEPRECATED_SEMVER, @@ -512,7 +457,6 @@ pub struct EarlyAttributes { impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CFG_ATTR, - MISMATCHED_TARGET_OS, EMPTY_LINE_AFTER_OUTER_ATTR, ]); @@ -523,7 +467,6 @@ impl EarlyLintPass for EarlyAttributes { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { check_deprecated_cfg_attr(cx, attr, self.msrv); - check_mismatched_target_os(cx, attr); } extract_msrv_attr!(EarlyContext); @@ -594,72 +537,6 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Opti } } -fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { - fn find_os(name: &str) -> Option<&'static str> { - UNIX_SYSTEMS - .iter() - .chain(NON_UNIX_SYSTEMS.iter()) - .find(|&&os| os == name) - .copied() - } - - fn is_unix(name: &str) -> bool { - UNIX_SYSTEMS.iter().any(|&os| os == name) - } - - fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> { - let mut mismatched = Vec::new(); - - for item in items { - if let NestedMetaItem::MetaItem(meta) = item { - match &meta.kind { - MetaItemKind::List(list) => { - mismatched.extend(find_mismatched_target_os(list)); - }, - MetaItemKind::Word => { - if_chain! { - if let Some(ident) = meta.ident(); - if let Some(os) = find_os(&*ident.name.as_str()); - then { - mismatched.push((os, ident.span)); - } - } - }, - MetaItemKind::NameValue(..) => {}, - } - } - } - - mismatched - } - - if_chain! { - if attr.has_name(sym::cfg); - if let Some(list) = attr.meta_item_list(); - let mismatched = find_mismatched_target_os(&list); - if !mismatched.is_empty(); - then { - let mess = "operating system used in target family position"; - - span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { - // Avoid showing the unix suggestion multiple times in case - // we have more than one mismatch for unix-like systems - let mut unix_suggested = false; - - for (os, span) in mismatched { - let sugg = format!("target_os = \"{}\"", os); - diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); - - if !unix_suggested && is_unix(os) { - diag.help("did you mean `unix`?"); - unix_suggested = true; - } - } - }); - } - } -} - fn is_lint_level(symbol: Symbol) -> bool { matches!(symbol, sym::allow | sym::warn | sym::deny | sym::forbid) } diff --git a/clippy_lints/src/cfg_target_os.rs b/clippy_lints/src/cfg_target_os.rs new file mode 100644 index 000000000000..386e2520538b --- /dev/null +++ b/clippy_lints/src/cfg_target_os.rs @@ -0,0 +1,178 @@ +//! checks for cfg `target_os` usages + +use clippy_utils::diagnostics::span_lint_and_then; +use if_chain::if_chain; +use rustc_ast::{Attribute, MacCall, MetaItemKind, NestedMetaItem}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_parse::parser; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::sym; + +static UNIX_SYSTEMS: &[&str] = &[ + "android", + "dragonfly", + "emscripten", + "freebsd", + "fuchsia", + "haiku", + "illumos", + "ios", + "l4re", + "linux", + "macos", + "netbsd", + "openbsd", + "redox", + "solaris", + "vxworks", +]; + +// NOTE: windows is excluded from the list because it's also a valid target family. +static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"]; + +declare_clippy_lint! { + /// ### What it does + /// Checks for cfg attributes having operating systems used in target family position. + /// + /// ### Why is this bad? + /// The configuration option will not be recognised and the related item will not be included + /// by the conditional compilation engine. + /// + /// ### Example + /// Bad: + /// ```rust + /// #[cfg(linux)] + /// fn conditional() { } + /// ``` + /// + /// Good: + /// ```rust + /// #[cfg(target_os = "linux")] + /// fn conditional() { } + /// ``` + /// + /// Or: + /// ```rust + /// #[cfg(unix)] + /// fn conditional() { } + /// ``` + /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details. + #[clippy::version = "1.45.0"] + pub MISMATCHED_TARGET_OS, + correctness, + "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`" +} + +declare_lint_pass!(CfgTargetOs => [MISMATCHED_TARGET_OS]); + +impl EarlyLintPass for CfgTargetOs { + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { + check_mismatched_target_os(cx, attr); + } + + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { + check_mismatched_target_os_mac(cx, mac); + } +} + +fn find_os(name: &str) -> Option<&'static str> { + UNIX_SYSTEMS + .iter() + .chain(NON_UNIX_SYSTEMS.iter()) + .find(|&&os| os == name) + .copied() +} + +fn is_unix(name: &str) -> bool { + UNIX_SYSTEMS.iter().any(|&os| os == name) +} + +fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> { + let mut mismatched = Vec::new(); + + for item in items { + if let NestedMetaItem::MetaItem(meta) = item { + match &meta.kind { + MetaItemKind::List(list) => { + mismatched.extend(find_mismatched_target_os(list)); + }, + MetaItemKind::Word => { + if_chain! { + if let Some(ident) = meta.ident(); + if let Some(os) = find_os(&*ident.name.as_str()); + then { + mismatched.push((os, ident.span)); + } + } + }, + MetaItemKind::NameValue(..) => {}, + } + } + } + + mismatched +} + +fn write_diagnoses(cx: &EarlyContext<'_>, mismatched: Vec<(&str, Span)>, span: Span) { + if !mismatched.is_empty() { + let mess = "operating system used in target family position"; + + span_lint_and_then(cx, MISMATCHED_TARGET_OS, span, mess, |diag| { + // Avoid showing the unix suggestion multiple times in case + // we have more than one mismatch for unix-like systems + let mut unix_suggested = false; + + for (os, span) in mismatched { + let sugg = format!("target_os = \"{}\"", os); + diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); + + if !unix_suggested && is_unix(os) { + diag.help("did you mean `unix`?"); + unix_suggested = true; + } + } + }); + } +} + +fn check_mismatched_target_os_mac(cx: &EarlyContext<'_>, mac: &MacCall) { + let args = &mac.args; + let inner = args.inner_tokens(); + let mut parser = parser::Parser::new(&cx.sess.parse_sess, inner, false, None); + let meta = parser.parse_meta_item().unwrap(); + + let mismatched = if let Some(items) = meta.meta_item_list() { + find_mismatched_target_os(items) + } else { + match &meta.kind { + MetaItemKind::List(list) => find_mismatched_target_os(list), + MetaItemKind::Word => { + if_chain! { + if let Some(ident) = meta.ident(); + if let Some(os) = find_os(&*ident.name.as_str()); + then { + vec![(os, ident.span)] + } else { + Vec::new() + } + } + }, + MetaItemKind::NameValue(..) => Vec::new(), + } + }; + + write_diagnoses(cx, mismatched, mac.span()); +} + +fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { + if_chain! { + if attr.has_name(sym::cfg); + if let Some(list) = attr.meta_item_list(); + let mismatched = find_mismatched_target_os(&list); + then { + write_diagnoses(cx, mismatched, attr.span); + } + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 9ac77d638799..6854d126a504 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -12,7 +12,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), LintId::of(attrs::USELESS_ATTRIBUTE), LintId::of(bit_mask::BAD_BIT_MASK), LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), @@ -26,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(casts::FN_TO_NUMERIC_CAST), LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(casts::UNNECESSARY_CAST), + LintId::of(cfg_target_os::MISMATCHED_TARGET_OS), LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(collapsible_if::COLLAPSIBLE_IF), LintId::of(collapsible_match::COLLAPSIBLE_MATCH), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index 4217fd3a3ea7..b534bf65ceef 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -7,12 +7,12 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(approx_const::APPROX_CONSTANT), LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), LintId::of(attrs::USELESS_ATTRIBUTE), LintId::of(bit_mask::BAD_BIT_MASK), LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(booleans::LOGIC_BUG), LintId::of(casts::CAST_REF_TO_MUT), + LintId::of(cfg_target_os::MISMATCHED_TARGET_OS), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), LintId::of(derive::DERIVE_HASH_XOR_EQ), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 68889f4f50a4..9004798e0fd3 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -47,7 +47,6 @@ store.register_lints(&[ attrs::DEPRECATED_SEMVER, attrs::EMPTY_LINE_AFTER_OUTER_ATTR, attrs::INLINE_ALWAYS, - attrs::MISMATCHED_TARGET_OS, attrs::USELESS_ATTRIBUTE, await_holding_invalid::AWAIT_HOLDING_LOCK, await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, @@ -75,6 +74,7 @@ store.register_lints(&[ casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, casts::PTR_AS_PTR, casts::UNNECESSARY_CAST, + cfg_target_os::MISMATCHED_TARGET_OS, checked_conversions::CHECKED_CONVERSIONS, cognitive_complexity::COGNITIVE_COMPLEXITY, collapsible_if::COLLAPSIBLE_ELSE_IF, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c6b14ecac431..50fa5b336ddf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -180,6 +180,7 @@ mod bytecount; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; mod casts; +mod cfg_target_os; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; @@ -427,6 +428,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se store.register_pre_expansion_pass(|| Box::new(write::Write::default())); store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv })); + store.register_pre_expansion_pass(|| Box::new(cfg_target_os::CfgTargetOs)); store.register_pre_expansion_pass(|| Box::new(dbg_macro::DbgMacro)); } diff --git a/tests/ui/mismatched_target_os_unix.fixed b/tests/ui/mismatched_target_os_unix.fixed index 7d9d406d99df..60a2d241463d 100644 --- a/tests/ui/mismatched_target_os_unix.fixed +++ b/tests/ui/mismatched_target_os_unix.fixed @@ -59,4 +59,31 @@ fn list() {} #[cfg(target_os = "freebsd")] fn correct() {} +fn macro_use() { + if cfg!(target_os = "android") {} + if cfg!(target_os = "dragonfly") {} + if cfg!(target_os = "emscripten") {} + if cfg!(target_os = "freebsd") {} + if cfg!(target_os = "fuchsia") {} + if cfg!(target_os = "haiku") {} + if cfg!(target_os = "illumos") {} + if cfg!(target_os = "ios") {} + if cfg!(target_os = "l4re") {} + if cfg!(target_os = "linux") {} + if cfg!(target_os = "macos") {} + if cfg!(target_os = "netbsd") {} + if cfg!(target_os = "openbsd") {} + if cfg!(target_os = "redox") {} + if cfg!(target_os = "solaris") {} + if cfg!(target_os = "vxworks") {} + if cfg!(target_os = "hermit") {} + if cfg!(target_os = "none") {} + if cfg!(target_os = "wasi") {} + if cfg!(any(target_os = "linux", target_os = "macos")) {} + if cfg!(all(not(any(target_os = "solaris", target_os = "linux")), target_os = "freebsd")) {} + + // correct use + if cfg!(target_os = "macos") {} +} + fn main() {} diff --git a/tests/ui/mismatched_target_os_unix.rs b/tests/ui/mismatched_target_os_unix.rs index c1177f1eedc6..2d8d487118a7 100644 --- a/tests/ui/mismatched_target_os_unix.rs +++ b/tests/ui/mismatched_target_os_unix.rs @@ -59,4 +59,31 @@ fn list() {} #[cfg(target_os = "freebsd")] fn correct() {} +fn macro_use() { + if cfg!(android) {} + if cfg!(dragonfly) {} + if cfg!(emscripten) {} + if cfg!(freebsd) {} + if cfg!(fuchsia) {} + if cfg!(haiku) {} + if cfg!(illumos) {} + if cfg!(ios) {} + if cfg!(l4re) {} + if cfg!(linux) {} + if cfg!(macos) {} + if cfg!(netbsd) {} + if cfg!(openbsd) {} + if cfg!(redox) {} + if cfg!(solaris) {} + if cfg!(vxworks) {} + if cfg!(hermit) {} + if cfg!(none) {} + if cfg!(wasi) {} + if cfg!(any(linux, macos)) {} + if cfg!(all(not(any(solaris, linux)), freebsd)) {} + + // correct use + if cfg!(target_os = "macos") {} +} + fn main() {} diff --git a/tests/ui/mismatched_target_os_unix.stderr b/tests/ui/mismatched_target_os_unix.stderr index 3534b53282f9..19eae259ec3b 100644 --- a/tests/ui/mismatched_target_os_unix.stderr +++ b/tests/ui/mismatched_target_os_unix.stderr @@ -179,5 +179,225 @@ help: try LL | #[cfg(all(not(any(solaris, linux)), target_os = "freebsd"))] | ~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 17 previous errors +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:63:8 + | +LL | if cfg!(android) {} + | ^^^^^-------^ + | | + | help: try: `target_os = "android"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:64:8 + | +LL | if cfg!(dragonfly) {} + | ^^^^^---------^ + | | + | help: try: `target_os = "dragonfly"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:65:8 + | +LL | if cfg!(emscripten) {} + | ^^^^^----------^ + | | + | help: try: `target_os = "emscripten"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:66:8 + | +LL | if cfg!(freebsd) {} + | ^^^^^-------^ + | | + | help: try: `target_os = "freebsd"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:67:8 + | +LL | if cfg!(fuchsia) {} + | ^^^^^-------^ + | | + | help: try: `target_os = "fuchsia"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:68:8 + | +LL | if cfg!(haiku) {} + | ^^^^^-----^ + | | + | help: try: `target_os = "haiku"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:69:8 + | +LL | if cfg!(illumos) {} + | ^^^^^-------^ + | | + | help: try: `target_os = "illumos"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:70:8 + | +LL | if cfg!(ios) {} + | ^^^^^---^ + | | + | help: try: `target_os = "ios"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:71:8 + | +LL | if cfg!(l4re) {} + | ^^^^^----^ + | | + | help: try: `target_os = "l4re"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:72:8 + | +LL | if cfg!(linux) {} + | ^^^^^-----^ + | | + | help: try: `target_os = "linux"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:73:8 + | +LL | if cfg!(macos) {} + | ^^^^^-----^ + | | + | help: try: `target_os = "macos"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:74:8 + | +LL | if cfg!(netbsd) {} + | ^^^^^------^ + | | + | help: try: `target_os = "netbsd"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:75:8 + | +LL | if cfg!(openbsd) {} + | ^^^^^-------^ + | | + | help: try: `target_os = "openbsd"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:76:8 + | +LL | if cfg!(redox) {} + | ^^^^^-----^ + | | + | help: try: `target_os = "redox"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:77:8 + | +LL | if cfg!(solaris) {} + | ^^^^^-------^ + | | + | help: try: `target_os = "solaris"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:78:8 + | +LL | if cfg!(vxworks) {} + | ^^^^^-------^ + | | + | help: try: `target_os = "vxworks"` + | + = help: did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:79:8 + | +LL | if cfg!(hermit) {} + | ^^^^^------^ + | | + | help: try: `target_os = "hermit"` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:80:8 + | +LL | if cfg!(none) {} + | ^^^^^----^ + | | + | help: try: `target_os = "none"` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:81:8 + | +LL | if cfg!(wasi) {} + | ^^^^^----^ + | | + | help: try: `target_os = "wasi"` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:82:8 + | +LL | if cfg!(any(linux, macos)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: did you mean `unix`? +help: try + | +LL | if cfg!(any(target_os = "linux", macos)) {} + | ~~~~~~~~~~~~~~~~~~~ +help: try + | +LL | if cfg!(any(linux, target_os = "macos")) {} + | ~~~~~~~~~~~~~~~~~~~ + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:83:8 + | +LL | if cfg!(all(not(any(solaris, linux)), freebsd)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: did you mean `unix`? +help: try + | +LL | if cfg!(all(not(any(target_os = "solaris", linux)), freebsd)) {} + | ~~~~~~~~~~~~~~~~~~~~~ +help: try + | +LL | if cfg!(all(not(any(solaris, target_os = "linux")), freebsd)) {} + | ~~~~~~~~~~~~~~~~~~~ +help: try + | +LL | if cfg!(all(not(any(solaris, linux)), target_os = "freebsd")) {} + | ~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 38 previous errors