diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index bff596e21e53a..b13423552c003 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -41,7 +41,7 @@ use lint::builtin::BuiltinLintDiagnostics; use session::{Session, DiagnosticMessageId}; use std::hash; use syntax::ast; -use syntax::codemap::MultiSpan; +use syntax::codemap::{MultiSpan, ExpnFormat}; use syntax::edition::Edition; use syntax::symbol::Symbol; use syntax::visit as ast_visit; @@ -568,6 +568,22 @@ pub fn struct_lint_level<'a>(sess: &'a Session, future_incompatible.reference); err.warn(&explanation); err.note(&citation); + + // If this lint is *not* a future incompatibility warning then we want to be + // sure to not be too noisy in some situations. If this code originates in a + // foreign macro, aka something that this crate did not itself author, then + // it's likely that there's nothing this crate can do about it. We probably + // want to skip the lint entirely. + // + // For some lints though (like unreachable code) there's clear actionable + // items to take care of (delete the macro invocation). As a result we have + // a few lints we whitelist here for allowing a lint even though it's in a + // foreign macro invocation. + } else if lint_id != LintId::of(builtin::UNREACHABLE_CODE) && + lint_id != LintId::of(builtin::DEPRECATED) { + if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) { + err.cancel(); + } } return err @@ -669,3 +685,32 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'a, 'tcx> { pub fn provide(providers: &mut Providers) { providers.lint_levels = lint_levels; } + +/// Returns whether `span` originates in a foreign crate's external macro. +/// +/// This is used to test whether a lint should be entirely aborted above. +pub fn in_external_macro(sess: &Session, span: Span) -> bool { + let info = match span.ctxt().outer().expn_info() { + Some(info) => info, + // no ExpnInfo means this span doesn't come from a macro + None => return false, + }; + + match info.format { + ExpnFormat::MacroAttribute(..) => return true, // definitely a plugin + ExpnFormat::CompilerDesugaring(_) => return true, // well, it's "external" + ExpnFormat::MacroBang(..) => {} // check below + } + + let def_site = match info.def_site { + Some(span) => span, + // no span for the def_site means it's an external macro + None => return true, + }; + + match sess.codemap().span_to_snippet(def_site) { + Ok(code) => !code.starts_with("macro_rules"), + // no snippet = external macro or compiler-builtin expansion + Err(_) => true, + } +} diff --git a/src/test/ui/lint/auxiliary/lints-in-foreign-macros.rs b/src/test/ui/lint/auxiliary/lints-in-foreign-macros.rs new file mode 100644 index 0000000000000..cf8e9c18de3c0 --- /dev/null +++ b/src/test/ui/lint/auxiliary/lints-in-foreign-macros.rs @@ -0,0 +1,24 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_export] +macro_rules! bar { + () => {use std::string::ToString;} +} + +#[macro_export] +macro_rules! baz { + ($i:item) => ($i) +} + +#[macro_export] +macro_rules! baz2 { + ($($i:tt)*) => ($($i)*) +} diff --git a/src/test/ui/lint/lints-in-foreign-macros.rs b/src/test/ui/lint/lints-in-foreign-macros.rs new file mode 100644 index 0000000000000..0f9003877cc06 --- /dev/null +++ b/src/test/ui/lint/lints-in-foreign-macros.rs @@ -0,0 +1,28 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:lints-in-foreign-macros.rs +// compile-pass + +#![warn(unused_imports)] + +#[macro_use] +extern crate lints_in_foreign_macros; + +macro_rules! foo { + () => {use std::string::ToString;} //~ WARN: unused import +} + +mod a { foo!(); } +mod b { bar!(); } +mod c { baz!(use std::string::ToString;); } //~ WARN: unused import +mod d { baz2!(use std::string::ToString;); } //~ WARN: unused import + +fn main() {} diff --git a/src/test/ui/lint/lints-in-foreign-macros.stderr b/src/test/ui/lint/lints-in-foreign-macros.stderr new file mode 100644 index 0000000000000..e9f6d3d381541 --- /dev/null +++ b/src/test/ui/lint/lints-in-foreign-macros.stderr @@ -0,0 +1,27 @@ +warning: unused import: `std::string::ToString` + --> $DIR/lints-in-foreign-macros.rs:20:16 + | +LL | () => {use std::string::ToString;} //~ WARN: unused import + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | mod a { foo!(); } + | ------- in this macro invocation + | +note: lint level defined here + --> $DIR/lints-in-foreign-macros.rs:14:9 + | +LL | #![warn(unused_imports)] + | ^^^^^^^^^^^^^^ + +warning: unused import: `std::string::ToString` + --> $DIR/lints-in-foreign-macros.rs:25:18 + | +LL | mod c { baz!(use std::string::ToString;); } //~ WARN: unused import + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `std::string::ToString` + --> $DIR/lints-in-foreign-macros.rs:26:19 + | +LL | mod d { baz2!(use std::string::ToString;); } //~ WARN: unused import + | ^^^^^^^^^^^^^^^^^^^^^ +