rustc: Polish off in_external_macro
This commit polishes off this new function to compile on newer rustc as well as update and add a suite of test cases to work with this new check for lints.
This commit is contained in:
parent
dd0808dd24
commit
8adf08c437
5 changed files with 112 additions and 39 deletions
|
@ -29,7 +29,7 @@ use self::TargetLint::*;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use rustc_data_structures::sync::{RwLock, ReadGuard};
|
use rustc_data_structures::sync::{RwLock, ReadGuard};
|
||||||
use lint::{EarlyLintPassObject, LateLintPassObject};
|
use lint::{EarlyLintPassObject, LateLintPassObject};
|
||||||
use lint::{self, Level, Lint, LintId, LintPass, LintBuffer};
|
use lint::{Level, Lint, LintId, LintPass, LintBuffer};
|
||||||
use lint::builtin::BuiltinLintDiagnostics;
|
use lint::builtin::BuiltinLintDiagnostics;
|
||||||
use lint::levels::{LintLevelSets, LintLevelsBuilder};
|
use lint::levels::{LintLevelSets, LintLevelsBuilder};
|
||||||
use middle::privacy::AccessLevels;
|
use middle::privacy::AccessLevels;
|
||||||
|
@ -468,15 +468,8 @@ pub trait LintContext<'tcx>: Sized {
|
||||||
|
|
||||||
/// Emit a lint at the appropriate level, for a particular span.
|
/// Emit a lint at the appropriate level, for a particular span.
|
||||||
fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) {
|
fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) {
|
||||||
match self.lints().future_incompatible(LintId::of(lint)) {
|
|
||||||
Some(_) => self.lookup_and_emit(lint, Some(span), msg),
|
|
||||||
None => {
|
|
||||||
if !lint::in_external_macro(lint, span) {
|
|
||||||
self.lookup_and_emit(lint, Some(span), msg);
|
self.lookup_and_emit(lint, Some(span), msg);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn struct_span_lint<S: Into<MultiSpan>>(&self,
|
fn struct_span_lint<S: Into<MultiSpan>>(&self,
|
||||||
lint: &'static Lint,
|
lint: &'static Lint,
|
||||||
|
|
|
@ -41,7 +41,7 @@ use lint::builtin::BuiltinLintDiagnostics;
|
||||||
use session::{Session, DiagnosticMessageId};
|
use session::{Session, DiagnosticMessageId};
|
||||||
use std::hash;
|
use std::hash;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::codemap::MultiSpan;
|
use syntax::codemap::{MultiSpan, ExpnFormat};
|
||||||
use syntax::edition::Edition;
|
use syntax::edition::Edition;
|
||||||
use syntax::symbol::Symbol;
|
use syntax::symbol::Symbol;
|
||||||
use syntax::visit as ast_visit;
|
use syntax::visit as ast_visit;
|
||||||
|
@ -54,8 +54,6 @@ pub use lint::context::{LateContext, EarlyContext, LintContext, LintStore,
|
||||||
check_crate, check_ast_crate,
|
check_crate, check_ast_crate,
|
||||||
FutureIncompatibleInfo, BufferedEarlyLint};
|
FutureIncompatibleInfo, BufferedEarlyLint};
|
||||||
|
|
||||||
use codemap::{ExpnFormat, ExpnInfo, Span };
|
|
||||||
|
|
||||||
/// Specification of a single lint.
|
/// Specification of a single lint.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Lint {
|
pub struct Lint {
|
||||||
|
@ -570,6 +568,22 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
|
||||||
future_incompatible.reference);
|
future_incompatible.reference);
|
||||||
err.warn(&explanation);
|
err.warn(&explanation);
|
||||||
err.note(&citation);
|
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
|
return err
|
||||||
|
@ -672,29 +686,31 @@ pub fn provide(providers: &mut Providers) {
|
||||||
providers.lint_levels = lint_levels;
|
providers.lint_levels = lint_levels;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn in_external_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool {
|
/// Returns whether `span` originates in a foreign crate's external macro.
|
||||||
/// Invokes `in_macro` with the expansion info of the given span slightly
|
///
|
||||||
/// heavy, try to use
|
/// This is used to test whether a lint should be entirely aborted above.
|
||||||
/// this after other checks have already happened.
|
pub fn in_external_macro(sess: &Session, span: Span) -> bool {
|
||||||
fn in_macro_ext<'a, T: LintContext<'a>>(cx: &T, info: &ExpnInfo) -> bool {
|
let info = match span.ctxt().outer().expn_info() {
|
||||||
// no ExpnInfo = no macro
|
Some(info) => info,
|
||||||
if let ExpnFormat::MacroAttribute(..) = info.callee.format {
|
// no ExpnInfo means this span doesn't come from a macro
|
||||||
// these are all plugins
|
None => return false,
|
||||||
return true;
|
};
|
||||||
}
|
|
||||||
// no span for the callee = external macro
|
match info.format {
|
||||||
info.callee.span.map_or(true, |span| {
|
ExpnFormat::MacroAttribute(..) => return true, // definitely a plugin
|
||||||
// no snippet = external macro or compiler-builtin expansion
|
ExpnFormat::CompilerDesugaring(_) => return true, // well, it's "external"
|
||||||
cx.sess()
|
ExpnFormat::MacroBang(..) => {} // check below
|
||||||
.codemap()
|
|
||||||
.span_to_snippet(span)
|
|
||||||
.ok()
|
|
||||||
.map_or(true, |code| !code.starts_with("macro_rules"))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
span.ctxt()
|
let def_site = match info.def_site {
|
||||||
.outer()
|
Some(span) => span,
|
||||||
.expn_info()
|
// no span for the def_site means it's an external macro
|
||||||
.map_or(false, |info| in_macro_ext(cx, &info))
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,18 @@
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
#![feature(nll)]
|
|
||||||
#![deny(elided_lifetime_in_path)]
|
|
||||||
|
|
||||||
fn main() {
|
#[macro_export]
|
||||||
format!("foo {}", 22)
|
macro_rules! bar {
|
||||||
|
() => {use std::string::ToString;}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! baz {
|
||||||
|
($i:item) => ($i)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! baz2 {
|
||||||
|
($($i:tt)*) => ($($i)*)
|
||||||
}
|
}
|
28
src/test/ui/lint/lints-in-foreign-macros.rs
Normal file
28
src/test/ui/lint/lints-in-foreign-macros.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {}
|
27
src/test/ui/lint/lints-in-foreign-macros.stderr
Normal file
27
src/test/ui/lint/lints-in-foreign-macros.stderr
Normal file
|
@ -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
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue