Skip to content

Commit 5d8a3e8

Browse files
committed
Auto merge of #5506 - ebroto:mismatched_target_os, r=flip1995
Implement mismatched_target_os lint I've extended the check suggested in the issue to all the currently supported operating systems instead of limiting it to `linux` and `macos`, let me know if we want to do this. Also, I've restored the text `There are over XXX lints ...` in the README as it was matched against by `cargo dev new_lint`. changelog: Added `mismatched_target_os` lint to warn when an operating system is used in target family position in a #[cfg] attribute Closes #3949
2 parents 87a6f3f + 303e7d1 commit 5d8a3e8

10 files changed

+582
-28
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,7 @@ Released 2018-09-13
14441444
[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
14451445
[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
14461446
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
1447+
[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
14471448
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
14481449
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
14491450
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items

clippy_lints/src/attrs.rs

+152-27
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,28 @@ use rustc_span::source_map::Span;
2020
use rustc_span::symbol::Symbol;
2121
use semver::Version;
2222

23+
static UNIX_SYSTEMS: &[&str] = &[
24+
"android",
25+
"dragonfly",
26+
"emscripten",
27+
"freebsd",
28+
"fuchsia",
29+
"haiku",
30+
"illumos",
31+
"ios",
32+
"l4re",
33+
"linux",
34+
"macos",
35+
"netbsd",
36+
"openbsd",
37+
"redox",
38+
"solaris",
39+
"vxworks",
40+
];
41+
42+
// NOTE: windows is excluded from the list because it's also a valid target family.
43+
static NON_UNIX_SYSTEMS: &[&str] = &["cloudabi", "hermit", "none", "wasi"];
44+
2345
declare_clippy_lint! {
2446
/// **What it does:** Checks for items annotated with `#[inline(always)]`,
2547
/// unless the annotated function is empty or simply panics.
@@ -189,6 +211,38 @@ declare_clippy_lint! {
189211
"usage of `cfg_attr(rustfmt)` instead of tool attributes"
190212
}
191213

214+
declare_clippy_lint! {
215+
/// **What it does:** Checks for cfg attributes having operating systems used in target family position.
216+
///
217+
/// **Why is this bad?** The configuration option will not be recognised and the related item will not be included
218+
/// by the conditional compilation engine.
219+
///
220+
/// **Known problems:** None.
221+
///
222+
/// **Example:**
223+
///
224+
/// Bad:
225+
/// ```rust
226+
/// #[cfg(linux)]
227+
/// fn conditional() { }
228+
/// ```
229+
///
230+
/// Good:
231+
/// ```rust
232+
/// #[cfg(target_os = "linux")]
233+
/// fn conditional() { }
234+
/// ```
235+
///
236+
/// Or:
237+
/// ```rust
238+
/// #[cfg(unix)]
239+
/// fn conditional() { }
240+
/// ```
241+
pub MISMATCHED_TARGET_OS,
242+
correctness,
243+
"usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
244+
}
245+
192246
declare_lint_pass!(Attributes => [
193247
INLINE_ALWAYS,
194248
DEPRECATED_SEMVER,
@@ -496,36 +550,107 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
496550
}
497551
}
498552

499-
declare_lint_pass!(DeprecatedCfgAttribute => [DEPRECATED_CFG_ATTR]);
553+
declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]);
500554

501-
impl EarlyLintPass for DeprecatedCfgAttribute {
555+
impl EarlyLintPass for EarlyAttributes {
502556
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
503-
if_chain! {
504-
// check cfg_attr
505-
if attr.check_name(sym!(cfg_attr));
506-
if let Some(items) = attr.meta_item_list();
507-
if items.len() == 2;
508-
// check for `rustfmt`
509-
if let Some(feature_item) = items[0].meta_item();
510-
if feature_item.check_name(sym!(rustfmt));
511-
// check for `rustfmt_skip` and `rustfmt::skip`
512-
if let Some(skip_item) = &items[1].meta_item();
513-
if skip_item.check_name(sym!(rustfmt_skip)) ||
514-
skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym!(skip);
515-
// Only lint outer attributes, because custom inner attributes are unstable
516-
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
517-
if let AttrStyle::Outer = attr.style;
518-
then {
519-
span_lint_and_sugg(
520-
cx,
521-
DEPRECATED_CFG_ATTR,
522-
attr.span,
523-
"`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
524-
"use",
525-
"#[rustfmt::skip]".to_string(),
526-
Applicability::MachineApplicable,
527-
);
557+
check_deprecated_cfg_attr(cx, attr);
558+
check_mismatched_target_os(cx, attr);
559+
}
560+
}
561+
562+
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
563+
if_chain! {
564+
// check cfg_attr
565+
if attr.check_name(sym!(cfg_attr));
566+
if let Some(items) = attr.meta_item_list();
567+
if items.len() == 2;
568+
// check for `rustfmt`
569+
if let Some(feature_item) = items[0].meta_item();
570+
if feature_item.check_name(sym!(rustfmt));
571+
// check for `rustfmt_skip` and `rustfmt::skip`
572+
if let Some(skip_item) = &items[1].meta_item();
573+
if skip_item.check_name(sym!(rustfmt_skip)) ||
574+
skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym!(skip);
575+
// Only lint outer attributes, because custom inner attributes are unstable
576+
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
577+
if let AttrStyle::Outer = attr.style;
578+
then {
579+
span_lint_and_sugg(
580+
cx,
581+
DEPRECATED_CFG_ATTR,
582+
attr.span,
583+
"`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
584+
"use",
585+
"#[rustfmt::skip]".to_string(),
586+
Applicability::MachineApplicable,
587+
);
588+
}
589+
}
590+
}
591+
592+
fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
593+
fn find_os(name: &str) -> Option<&'static str> {
594+
UNIX_SYSTEMS
595+
.iter()
596+
.chain(NON_UNIX_SYSTEMS.iter())
597+
.find(|&&os| os == name)
598+
.copied()
599+
}
600+
601+
fn is_unix(name: &str) -> bool {
602+
UNIX_SYSTEMS.iter().any(|&os| os == name)
603+
}
604+
605+
fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
606+
let mut mismatched = Vec::new();
607+
608+
for item in items {
609+
if let NestedMetaItem::MetaItem(meta) = item {
610+
match &meta.kind {
611+
MetaItemKind::List(list) => {
612+
mismatched.extend(find_mismatched_target_os(&list));
613+
},
614+
MetaItemKind::Word => {
615+
if_chain! {
616+
if let Some(ident) = meta.ident();
617+
if let Some(os) = find_os(&*ident.name.as_str());
618+
then {
619+
mismatched.push((os, ident.span));
620+
}
621+
}
622+
},
623+
_ => {},
624+
}
528625
}
529626
}
627+
628+
mismatched
629+
}
630+
631+
if_chain! {
632+
if attr.check_name(sym!(cfg));
633+
if let Some(list) = attr.meta_item_list();
634+
let mismatched = find_mismatched_target_os(&list);
635+
if !mismatched.is_empty();
636+
then {
637+
let mess = "operating system used in target family position";
638+
639+
span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, &mess, |diag| {
640+
// Avoid showing the unix suggestion multiple times in case
641+
// we have more than one mismatch for unix-like systems
642+
let mut unix_suggested = false;
643+
644+
for (os, span) in mismatched {
645+
let sugg = format!("target_os = \"{}\"", os);
646+
diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
647+
648+
if !unix_suggested && is_unix(os) {
649+
diag.help("Did you mean `unix`?");
650+
unix_suggested = true;
651+
}
652+
}
653+
});
654+
}
530655
}
531656
}

clippy_lints/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &Co
350350
store.register_pre_expansion_pass(move || box non_expressive_names::NonExpressiveNames {
351351
single_char_binding_names_threshold,
352352
});
353-
store.register_pre_expansion_pass(|| box attrs::DeprecatedCfgAttribute);
353+
store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
354354
store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
355355
}
356356

@@ -496,6 +496,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
496496
&attrs::DEPRECATED_SEMVER,
497497
&attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
498498
&attrs::INLINE_ALWAYS,
499+
&attrs::MISMATCHED_TARGET_OS,
499500
&attrs::UNKNOWN_CLIPPY_LINTS,
500501
&attrs::USELESS_ATTRIBUTE,
501502
&await_holding_lock::AWAIT_HOLDING_LOCK,
@@ -1190,6 +1191,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11901191
LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
11911192
LintId::of(&attrs::DEPRECATED_CFG_ATTR),
11921193
LintId::of(&attrs::DEPRECATED_SEMVER),
1194+
LintId::of(&attrs::MISMATCHED_TARGET_OS),
11931195
LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
11941196
LintId::of(&attrs::USELESS_ATTRIBUTE),
11951197
LintId::of(&bit_mask::BAD_BIT_MASK),
@@ -1610,6 +1612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
16101612
LintId::of(&approx_const::APPROX_CONSTANT),
16111613
LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
16121614
LintId::of(&attrs::DEPRECATED_SEMVER),
1615+
LintId::of(&attrs::MISMATCHED_TARGET_OS),
16131616
LintId::of(&attrs::USELESS_ATTRIBUTE),
16141617
LintId::of(&bit_mask::BAD_BIT_MASK),
16151618
LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),

src/lintlist/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
12281228
deprecation: None,
12291229
module: "minmax",
12301230
},
1231+
Lint {
1232+
name: "mismatched_target_os",
1233+
group: "correctness",
1234+
desc: "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`",
1235+
deprecation: None,
1236+
module: "attrs",
1237+
},
12311238
Lint {
12321239
name: "misrefactored_assign_op",
12331240
group: "complexity",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::mismatched_target_os)]
4+
#![allow(unused)]
5+
6+
#[cfg(target_os = "cloudabi")]
7+
fn cloudabi() {}
8+
9+
#[cfg(target_os = "hermit")]
10+
fn hermit() {}
11+
12+
#[cfg(target_os = "wasi")]
13+
fn wasi() {}
14+
15+
#[cfg(target_os = "none")]
16+
fn none() {}
17+
18+
// list with conditions
19+
#[cfg(all(not(any(windows, target_os = "cloudabi")), target_os = "wasi"))]
20+
fn list() {}
21+
22+
// windows is a valid target family, should be ignored
23+
#[cfg(windows)]
24+
fn windows() {}
25+
26+
// correct use, should be ignored
27+
#[cfg(target_os = "hermit")]
28+
fn correct() {}
29+
30+
fn main() {}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::mismatched_target_os)]
4+
#![allow(unused)]
5+
6+
#[cfg(cloudabi)]
7+
fn cloudabi() {}
8+
9+
#[cfg(hermit)]
10+
fn hermit() {}
11+
12+
#[cfg(wasi)]
13+
fn wasi() {}
14+
15+
#[cfg(none)]
16+
fn none() {}
17+
18+
// list with conditions
19+
#[cfg(all(not(any(windows, cloudabi)), wasi))]
20+
fn list() {}
21+
22+
// windows is a valid target family, should be ignored
23+
#[cfg(windows)]
24+
fn windows() {}
25+
26+
// correct use, should be ignored
27+
#[cfg(target_os = "hermit")]
28+
fn correct() {}
29+
30+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
error: operating system used in target family position
2+
--> $DIR/mismatched_target_os_non_unix.rs:6:1
3+
|
4+
LL | #[cfg(cloudabi)]
5+
| ^^^^^^--------^^
6+
| |
7+
| help: try: `target_os = "cloudabi"`
8+
|
9+
= note: `-D clippy::mismatched-target-os` implied by `-D warnings`
10+
11+
error: operating system used in target family position
12+
--> $DIR/mismatched_target_os_non_unix.rs:9:1
13+
|
14+
LL | #[cfg(hermit)]
15+
| ^^^^^^------^^
16+
| |
17+
| help: try: `target_os = "hermit"`
18+
19+
error: operating system used in target family position
20+
--> $DIR/mismatched_target_os_non_unix.rs:12:1
21+
|
22+
LL | #[cfg(wasi)]
23+
| ^^^^^^----^^
24+
| |
25+
| help: try: `target_os = "wasi"`
26+
27+
error: operating system used in target family position
28+
--> $DIR/mismatched_target_os_non_unix.rs:15:1
29+
|
30+
LL | #[cfg(none)]
31+
| ^^^^^^----^^
32+
| |
33+
| help: try: `target_os = "none"`
34+
35+
error: operating system used in target family position
36+
--> $DIR/mismatched_target_os_non_unix.rs:19:1
37+
|
38+
LL | #[cfg(all(not(any(windows, cloudabi)), wasi))]
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40+
|
41+
help: try
42+
|
43+
LL | #[cfg(all(not(any(windows, target_os = "cloudabi")), wasi))]
44+
| ^^^^^^^^^^^^^^^^^^^^^^
45+
help: try
46+
|
47+
LL | #[cfg(all(not(any(windows, cloudabi)), target_os = "wasi"))]
48+
| ^^^^^^^^^^^^^^^^^^
49+
50+
error: aborting due to 5 previous errors
51+

0 commit comments

Comments
 (0)