@@ -20,6 +20,28 @@ use rustc_span::source_map::Span;
2020use rustc_span:: symbol:: Symbol ;
2121use 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+
2345declare_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+
192246declare_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}
0 commit comments