@@ -41,7 +41,7 @@ use lint::builtin::BuiltinLintDiagnostics;
4141use session:: { Session , DiagnosticMessageId } ;
4242use std:: hash;
4343use syntax:: ast;
44- use syntax:: codemap:: MultiSpan ;
44+ use syntax:: codemap:: { MultiSpan , ExpnFormat } ;
4545use syntax:: edition:: Edition ;
4646use syntax:: symbol:: Symbol ;
4747use syntax:: visit as ast_visit;
@@ -54,8 +54,6 @@ pub use lint::context::{LateContext, EarlyContext, LintContext, LintStore,
5454 check_crate, check_ast_crate,
5555 FutureIncompatibleInfo , BufferedEarlyLint } ;
5656
57- use codemap:: { ExpnFormat , ExpnInfo , Span } ;
58-
5957/// Specification of a single lint.
6058#[ derive( Copy , Clone , Debug ) ]
6159pub struct Lint {
@@ -570,6 +568,22 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
570568 future_incompatible. reference) ;
571569 err. warn ( & explanation) ;
572570 err. note ( & citation) ;
571+
572+ // If this lint is *not* a future incompatibility warning then we want to be
573+ // sure to not be too noisy in some situations. If this code originates in a
574+ // foreign macro, aka something that this crate did not itself author, then
575+ // it's likely that there's nothing this crate can do about it. We probably
576+ // want to skip the lint entirely.
577+ //
578+ // For some lints though (like unreachable code) there's clear actionable
579+ // items to take care of (delete the macro invocation). As a result we have
580+ // a few lints we whitelist here for allowing a lint even though it's in a
581+ // foreign macro invocation.
582+ } else if lint_id != LintId :: of ( builtin:: UNREACHABLE_CODE ) &&
583+ lint_id != LintId :: of ( builtin:: DEPRECATED ) {
584+ if err. span . primary_spans ( ) . iter ( ) . any ( |s| in_external_macro ( sess, * s) ) {
585+ err. cancel ( ) ;
586+ }
573587 }
574588
575589 return err
@@ -672,29 +686,31 @@ pub fn provide(providers: &mut Providers) {
672686 providers. lint_levels = lint_levels;
673687}
674688
675- pub fn in_external_macro < ' a , T : LintContext < ' a > > ( cx : & T , span : Span ) -> bool {
676- /// Invokes `in_macro` with the expansion info of the given span slightly
677- /// heavy, try to use
678- /// this after other checks have already happened.
679- fn in_macro_ext < ' a , T : LintContext < ' a > > ( cx : & T , info : & ExpnInfo ) -> bool {
680- // no ExpnInfo = no macro
681- if let ExpnFormat :: MacroAttribute ( ..) = info. callee . format {
682- // these are all plugins
683- return true ;
684- }
685- // no span for the callee = external macro
686- info. callee . span . map_or ( true , |span| {
687- // no snippet = external macro or compiler-builtin expansion
688- cx. sess ( )
689- . codemap ( )
690- . span_to_snippet ( span)
691- . ok ( )
692- . map_or ( true , |code| !code. starts_with ( "macro_rules" ) )
693- } )
689+ /// Returns whether `span` originates in a foreign crate's external macro.
690+ ///
691+ /// This is used to test whether a lint should be entirely aborted above.
692+ pub fn in_external_macro ( sess : & Session , span : Span ) -> bool {
693+ let info = match span. ctxt ( ) . outer ( ) . expn_info ( ) {
694+ Some ( info) => info,
695+ // no ExpnInfo means this span doesn't come from a macro
696+ None => return false ,
697+ } ;
698+
699+ match info. format {
700+ ExpnFormat :: MacroAttribute ( ..) => return true , // definitely a plugin
701+ ExpnFormat :: CompilerDesugaring ( _) => return true , // well, it's "external"
702+ ExpnFormat :: MacroBang ( ..) => { } // check below
694703 }
695704
696- span. ctxt ( )
697- . outer ( )
698- . expn_info ( )
699- . map_or ( false , |info| in_macro_ext ( cx, & info) )
705+ let def_site = match info. def_site {
706+ Some ( span) => span,
707+ // no span for the def_site means it's an external macro
708+ None => return true ,
709+ } ;
710+
711+ match sess. codemap ( ) . span_to_snippet ( def_site) {
712+ Ok ( code) => !code. starts_with ( "macro_rules" ) ,
713+ // no snippet = external macro or compiler-builtin expansion
714+ Err ( _) => true ,
715+ }
700716}
0 commit comments