11use std:: cmp;
22
33use crate :: ich:: StableHashingContext ;
4- use crate :: lint;
54use crate :: lint:: context:: { CheckLintNameResult , LintStore } ;
65use rustc_data_structures:: fx:: FxHashMap ;
76use 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 } ;
98use rustc_hir:: HirId ;
109use 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 } ;
1313use rustc_span:: symbol:: { sym, Symbol } ;
1414use rustc_span:: Span ;
1515use syntax:: ast;
@@ -326,7 +326,7 @@ impl<'a> LintLevelsBuilder<'a> {
326326 Also `cfg_attr(cargo-clippy)` won't be necessary anymore",
327327 name
328328 ) ;
329- lint :: struct_lint_level (
329+ struct_lint_level (
330330 self . sess ,
331331 lint,
332332 lvl,
@@ -366,7 +366,7 @@ impl<'a> LintLevelsBuilder<'a> {
366366 let lint = builtin:: RENAMED_AND_REMOVED_LINTS ;
367367 let ( level, src) =
368368 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 (
370370 self . sess ,
371371 lint,
372372 level,
@@ -389,7 +389,7 @@ impl<'a> LintLevelsBuilder<'a> {
389389 let ( level, src) =
390390 self . sets . get_lint_level ( lint, self . cur , Some ( & specs) , self . sess ) ;
391391 let msg = format ! ( "unknown lint: `{}`" , name) ;
392- let mut db = lint :: struct_lint_level (
392+ let mut db = struct_lint_level (
393393 self . sess ,
394394 lint,
395395 level,
@@ -480,7 +480,7 @@ impl<'a> LintLevelsBuilder<'a> {
480480 msg : & str ,
481481 ) -> DiagnosticBuilder < ' a > {
482482 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)
484484 }
485485
486486 /// Registers the ID provided with the current set of lints stored in
@@ -553,3 +553,155 @@ impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
553553 } )
554554 }
555555}
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