@@ -20,6 +20,30 @@ use rustc_span::source_map::Span;
2020use rustc_span:: symbol:: Symbol ;
2121use semver:: Version ;
2222
23+ // NOTE: windows is excluded from the list because it's also a valid target family.
24+ static OPERATING_SYSTEMS : & [ & str ] = & [
25+ "android" ,
26+ "cloudabi" ,
27+ "dragonfly" ,
28+ "emscripten" ,
29+ "freebsd" ,
30+ "fuchsia" ,
31+ "haiku" ,
32+ "hermit" ,
33+ "illumos" ,
34+ "ios" ,
35+ "l4re" ,
36+ "linux" ,
37+ "macos" ,
38+ "netbsd" ,
39+ "none" ,
40+ "openbsd" ,
41+ "redox" ,
42+ "solaris" ,
43+ "vxworks" ,
44+ "wasi" ,
45+ ] ;
46+
2347declare_clippy_lint ! {
2448 /// **What it does:** Checks for items annotated with `#[inline(always)]`,
2549 /// unless the annotated function is empty or simply panics.
@@ -189,6 +213,38 @@ declare_clippy_lint! {
189213 "usage of `cfg_attr(rustfmt)` instead of tool attributes"
190214}
191215
216+ declare_clippy_lint ! {
217+ /// **What it does:** Checks for cfg attributes having operating systems used in target family position.
218+ ///
219+ /// **Why is this bad?** The configuration option will not be recognised and the related item will not be included
220+ /// by the conditional compilation engine.
221+ ///
222+ /// **Known problems:** None.
223+ ///
224+ /// **Example:**
225+ ///
226+ /// Bad:
227+ /// ```rust
228+ /// #[cfg(linux)]
229+ /// fn conditional() { }
230+ /// ```
231+ ///
232+ /// Good:
233+ /// ```rust
234+ /// #[cfg(target_os = "linux")]
235+ /// fn conditional() { }
236+ /// ```
237+ ///
238+ /// Or:
239+ /// ```rust
240+ /// #[cfg(unix)]
241+ /// fn conditional() { }
242+ /// ```
243+ pub MISMATCHED_TARGET_OS ,
244+ correctness,
245+ "usage of `cfg(operating_system)` instead of `cfg(target_os = \" operating_system\" )`"
246+ }
247+
192248declare_lint_pass ! ( Attributes => [
193249 INLINE_ALWAYS ,
194250 DEPRECATED_SEMVER ,
@@ -496,35 +552,82 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
496552 }
497553}
498554
499- declare_lint_pass ! ( DeprecatedCfgAttribute => [ DEPRECATED_CFG_ATTR ] ) ;
555+ declare_lint_pass ! ( EarlyAttributes => [ DEPRECATED_CFG_ATTR , MISMATCHED_TARGET_OS ] ) ;
500556
501- impl EarlyLintPass for DeprecatedCfgAttribute {
557+ impl EarlyLintPass for EarlyAttributes {
502558 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- ) ;
559+ check_deprecated_cfg_attr ( cx, attr) ;
560+ check_mismatched_target_os ( cx, attr) ;
561+ }
562+ }
563+
564+ fn check_deprecated_cfg_attr ( cx : & EarlyContext < ' _ > , attr : & Attribute ) {
565+ if_chain ! {
566+ // check cfg_attr
567+ if attr. check_name( sym!( cfg_attr) ) ;
568+ if let Some ( items) = attr. meta_item_list( ) ;
569+ if items. len( ) == 2 ;
570+ // check for `rustfmt`
571+ if let Some ( feature_item) = items[ 0 ] . meta_item( ) ;
572+ if feature_item. check_name( sym!( rustfmt) ) ;
573+ // check for `rustfmt_skip` and `rustfmt::skip`
574+ if let Some ( skip_item) = & items[ 1 ] . meta_item( ) ;
575+ if skip_item. check_name( sym!( rustfmt_skip) ) ||
576+ skip_item. path. segments. last( ) . expect( "empty path in attribute" ) . ident. name == sym!( skip) ;
577+ // Only lint outer attributes, because custom inner attributes are unstable
578+ // Tracking issue: https://github.com/rust-lang/rust/issues/54726
579+ if let AttrStyle :: Outer = attr. style;
580+ then {
581+ span_lint_and_sugg(
582+ cx,
583+ DEPRECATED_CFG_ATTR ,
584+ attr. span,
585+ "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes" ,
586+ "use" ,
587+ "#[rustfmt::skip]" . to_string( ) ,
588+ Applicability :: MachineApplicable ,
589+ ) ;
590+ }
591+ }
592+ }
593+
594+ fn check_mismatched_target_os ( cx : & EarlyContext < ' _ > , attr : & Attribute ) {
595+ fn find_mismatched_target_os ( items : & [ NestedMetaItem ] ) -> Vec < ( & str , Span ) > {
596+ let mut mismatched = Vec :: new ( ) ;
597+ for item in items {
598+ if let NestedMetaItem :: MetaItem ( meta) = item {
599+ match & meta. kind {
600+ MetaItemKind :: List ( list) => {
601+ mismatched. extend ( find_mismatched_target_os ( & list) ) ;
602+ } ,
603+ MetaItemKind :: Word => {
604+ if let Some ( ident) = meta. ident ( ) {
605+ let name = & * ident. name . as_str ( ) ;
606+ if let Some ( os) = OPERATING_SYSTEMS . iter ( ) . find ( |& & os| os == name) {
607+ mismatched. push ( ( os, ident. span ) ) ;
608+ }
609+ }
610+ } ,
611+ _ => { } ,
612+ }
613+ }
614+ }
615+ mismatched
616+ }
617+
618+ if_chain ! {
619+ if attr. check_name( sym!( cfg) ) ;
620+ if let Some ( list) = attr. meta_item_list( ) ;
621+ then {
622+ let mismatched = find_mismatched_target_os( & list) ;
623+ for ( os, span) in mismatched {
624+ let mess = format!( "`{}` is not a valid target family" , os) ;
625+ let sugg = format!( "target_os = \" {}\" " , os) ;
626+
627+ span_lint_and_then( cx, MISMATCHED_TARGET_OS , span, & mess, |diag| {
628+ diag. span_suggestion( span, "try" , sugg, Applicability :: MaybeIncorrect ) ;
629+ diag. help( "Did you mean `unix`?" ) ;
630+ } ) ;
528631 }
529632 }
530633 }
0 commit comments