@@ -20,6 +20,28 @@ use rustc_span::source_map::Span;
20
20
use rustc_span:: symbol:: Symbol ;
21
21
use semver:: Version ;
22
22
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
+
23
45
declare_clippy_lint ! {
24
46
/// **What it does:** Checks for items annotated with `#[inline(always)]`,
25
47
/// unless the annotated function is empty or simply panics.
@@ -189,6 +211,38 @@ declare_clippy_lint! {
189
211
"usage of `cfg_attr(rustfmt)` instead of tool attributes"
190
212
}
191
213
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
+
192
246
declare_lint_pass ! ( Attributes => [
193
247
INLINE_ALWAYS ,
194
248
DEPRECATED_SEMVER ,
@@ -496,36 +550,107 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
496
550
}
497
551
}
498
552
499
- declare_lint_pass ! ( DeprecatedCfgAttribute => [ DEPRECATED_CFG_ATTR ] ) ;
553
+ declare_lint_pass ! ( EarlyAttributes => [ DEPRECATED_CFG_ATTR , MISMATCHED_TARGET_OS ] ) ;
500
554
501
- impl EarlyLintPass for DeprecatedCfgAttribute {
555
+ impl EarlyLintPass for EarlyAttributes {
502
556
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
+ }
528
625
}
529
626
}
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
+ }
530
655
}
531
656
}
0 commit comments