1
1
use std:: cmp;
2
2
3
3
use crate :: ich:: StableHashingContext ;
4
- use crate :: lint;
5
4
use crate :: lint:: context:: { CheckLintNameResult , LintStore } ;
6
5
use rustc_data_structures:: fx:: FxHashMap ;
7
6
use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher } ;
8
- use rustc_errors:: { struct_span_err, Applicability , DiagnosticBuilder } ;
7
+ use rustc_errors:: { struct_span_err, Applicability , DiagnosticBuilder , DiagnosticId } ;
9
8
use rustc_hir:: HirId ;
10
9
use rustc_session:: lint:: { builtin, Level , Lint , LintId } ;
11
- use rustc_session:: Session ;
12
- use rustc_span:: source_map:: MultiSpan ;
10
+ use rustc_session:: { DiagnosticMessageId , Session } ;
11
+ use rustc_span:: hygiene:: MacroKind ;
12
+ use rustc_span:: source_map:: { DesugaringKind , ExpnKind , MultiSpan } ;
13
13
use rustc_span:: symbol:: { sym, Symbol } ;
14
14
use rustc_span:: Span ;
15
15
use syntax:: ast;
@@ -326,7 +326,7 @@ impl<'a> LintLevelsBuilder<'a> {
326
326
Also `cfg_attr(cargo-clippy)` won't be necessary anymore",
327
327
name
328
328
) ;
329
- lint :: struct_lint_level (
329
+ struct_lint_level (
330
330
self . sess ,
331
331
lint,
332
332
lvl,
@@ -366,7 +366,7 @@ impl<'a> LintLevelsBuilder<'a> {
366
366
let lint = builtin:: RENAMED_AND_REMOVED_LINTS ;
367
367
let ( level, src) =
368
368
self . sets . get_lint_level ( lint, self . cur , Some ( & specs) , & sess) ;
369
- let mut err = lint :: struct_lint_level (
369
+ let mut err = struct_lint_level (
370
370
self . sess ,
371
371
lint,
372
372
level,
@@ -389,7 +389,7 @@ impl<'a> LintLevelsBuilder<'a> {
389
389
let ( level, src) =
390
390
self . sets . get_lint_level ( lint, self . cur , Some ( & specs) , self . sess ) ;
391
391
let msg = format ! ( "unknown lint: `{}`" , name) ;
392
- let mut db = lint :: struct_lint_level (
392
+ let mut db = struct_lint_level (
393
393
self . sess ,
394
394
lint,
395
395
level,
@@ -480,7 +480,7 @@ impl<'a> LintLevelsBuilder<'a> {
480
480
msg : & str ,
481
481
) -> DiagnosticBuilder < ' a > {
482
482
let ( level, src) = self . sets . get_lint_level ( lint, self . cur , None , self . sess ) ;
483
- lint :: struct_lint_level ( self . sess , lint, level, src, span, msg)
483
+ struct_lint_level ( self . sess , lint, level, src, span, msg)
484
484
}
485
485
486
486
/// Registers the ID provided with the current set of lints stored in
@@ -553,3 +553,155 @@ impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
553
553
} )
554
554
}
555
555
}
556
+
557
+ pub fn struct_lint_level < ' a > (
558
+ sess : & ' a Session ,
559
+ lint : & ' static Lint ,
560
+ level : Level ,
561
+ src : LintSource ,
562
+ span : Option < MultiSpan > ,
563
+ msg : & str ,
564
+ ) -> DiagnosticBuilder < ' a > {
565
+ let mut err = match ( level, span) {
566
+ ( Level :: Allow , _) => return sess. diagnostic ( ) . struct_dummy ( ) ,
567
+ ( Level :: Warn , Some ( span) ) => sess. struct_span_warn ( span, msg) ,
568
+ ( Level :: Warn , None ) => sess. struct_warn ( msg) ,
569
+ ( Level :: Deny , Some ( span) ) | ( Level :: Forbid , Some ( span) ) => sess. struct_span_err ( span, msg) ,
570
+ ( Level :: Deny , None ) | ( Level :: Forbid , None ) => sess. struct_err ( msg) ,
571
+ } ;
572
+
573
+ // Check for future incompatibility lints and issue a stronger warning.
574
+ let lint_id = LintId :: of ( lint) ;
575
+ let future_incompatible = lint. future_incompatible ;
576
+
577
+ // If this code originates in a foreign macro, aka something that this crate
578
+ // did not itself author, then it's likely that there's nothing this crate
579
+ // can do about it. We probably want to skip the lint entirely.
580
+ if err. span . primary_spans ( ) . iter ( ) . any ( |s| in_external_macro ( sess, * s) ) {
581
+ // Any suggestions made here are likely to be incorrect, so anything we
582
+ // emit shouldn't be automatically fixed by rustfix.
583
+ err. allow_suggestions ( false ) ;
584
+
585
+ // If this is a future incompatible lint it'll become a hard error, so
586
+ // we have to emit *something*. Also allow lints to whitelist themselves
587
+ // on a case-by-case basis for emission in a foreign macro.
588
+ if future_incompatible. is_none ( ) && !lint. report_in_external_macro {
589
+ err. cancel ( ) ;
590
+ // Don't continue further, since we don't want to have
591
+ // `diag_span_note_once` called for a diagnostic that isn't emitted.
592
+ return err;
593
+ }
594
+ }
595
+
596
+ let name = lint. name_lower ( ) ;
597
+ match src {
598
+ LintSource :: Default => {
599
+ sess. diag_note_once (
600
+ & mut err,
601
+ DiagnosticMessageId :: from ( lint) ,
602
+ & format ! ( "`#[{}({})]` on by default" , level. as_str( ) , name) ,
603
+ ) ;
604
+ }
605
+ LintSource :: CommandLine ( lint_flag_val) => {
606
+ let flag = match level {
607
+ Level :: Warn => "-W" ,
608
+ Level :: Deny => "-D" ,
609
+ Level :: Forbid => "-F" ,
610
+ Level :: Allow => panic ! ( ) ,
611
+ } ;
612
+ let hyphen_case_lint_name = name. replace ( "_" , "-" ) ;
613
+ if lint_flag_val. as_str ( ) == name {
614
+ sess. diag_note_once (
615
+ & mut err,
616
+ DiagnosticMessageId :: from ( lint) ,
617
+ & format ! (
618
+ "requested on the command line with `{} {}`" ,
619
+ flag, hyphen_case_lint_name
620
+ ) ,
621
+ ) ;
622
+ } else {
623
+ let hyphen_case_flag_val = lint_flag_val. as_str ( ) . replace ( "_" , "-" ) ;
624
+ sess. diag_note_once (
625
+ & mut err,
626
+ DiagnosticMessageId :: from ( lint) ,
627
+ & format ! (
628
+ "`{} {}` implied by `{} {}`" ,
629
+ flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
630
+ ) ,
631
+ ) ;
632
+ }
633
+ }
634
+ LintSource :: Node ( lint_attr_name, src, reason) => {
635
+ if let Some ( rationale) = reason {
636
+ err. note ( & rationale. as_str ( ) ) ;
637
+ }
638
+ sess. diag_span_note_once (
639
+ & mut err,
640
+ DiagnosticMessageId :: from ( lint) ,
641
+ src,
642
+ "lint level defined here" ,
643
+ ) ;
644
+ if lint_attr_name. as_str ( ) != name {
645
+ let level_str = level. as_str ( ) ;
646
+ sess. diag_note_once (
647
+ & mut err,
648
+ DiagnosticMessageId :: from ( lint) ,
649
+ & format ! (
650
+ "`#[{}({})]` implied by `#[{}({})]`" ,
651
+ level_str, name, level_str, lint_attr_name
652
+ ) ,
653
+ ) ;
654
+ }
655
+ }
656
+ }
657
+
658
+ err. code ( DiagnosticId :: Lint ( name) ) ;
659
+
660
+ if let Some ( future_incompatible) = future_incompatible {
661
+ const STANDARD_MESSAGE : & str = "this was previously accepted by the compiler but is being phased out; \
662
+ it will become a hard error";
663
+
664
+ let explanation = if lint_id == LintId :: of ( builtin:: UNSTABLE_NAME_COLLISIONS ) {
665
+ "once this method is added to the standard library, \
666
+ the ambiguity may cause an error or change in behavior!"
667
+ . to_owned ( )
668
+ } else if lint_id == LintId :: of ( builtin:: MUTABLE_BORROW_RESERVATION_CONFLICT ) {
669
+ "this borrowing pattern was not meant to be accepted, \
670
+ and may become a hard error in the future"
671
+ . to_owned ( )
672
+ } else if let Some ( edition) = future_incompatible. edition {
673
+ format ! ( "{} in the {} edition!" , STANDARD_MESSAGE , edition)
674
+ } else {
675
+ format ! ( "{} in a future release!" , STANDARD_MESSAGE )
676
+ } ;
677
+ let citation = format ! ( "for more information, see {}" , future_incompatible. reference) ;
678
+ err. warn ( & explanation) ;
679
+ err. note ( & citation) ;
680
+ }
681
+
682
+ return err;
683
+ }
684
+
685
+ /// Returns whether `span` originates in a foreign crate's external macro.
686
+ ///
687
+ /// This is used to test whether a lint should not even begin to figure out whether it should
688
+ /// be reported on the current node.
689
+ pub fn in_external_macro ( sess : & Session , span : Span ) -> bool {
690
+ let expn_data = span. ctxt ( ) . outer_expn_data ( ) ;
691
+ match expn_data. kind {
692
+ ExpnKind :: Root | ExpnKind :: Desugaring ( DesugaringKind :: ForLoop ) => false ,
693
+ ExpnKind :: AstPass ( _) | ExpnKind :: Desugaring ( _) => true , // well, it's "external"
694
+ ExpnKind :: Macro ( MacroKind :: Bang , _) => {
695
+ if expn_data. def_site . is_dummy ( ) {
696
+ // Dummy span for the `def_site` means it's an external macro.
697
+ return true ;
698
+ }
699
+ match sess. source_map ( ) . span_to_snippet ( expn_data. def_site ) {
700
+ Ok ( code) => !code. starts_with ( "macro_rules" ) ,
701
+ // No snippet means external macro or compiler-builtin expansion.
702
+ Err ( _) => true ,
703
+ }
704
+ }
705
+ ExpnKind :: Macro ( ..) => true , // definitely a plugin
706
+ }
707
+ }
0 commit comments