Skip to content

Commit 8adf08c

Browse files
committed
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.
1 parent dd0808d commit 8adf08c

File tree

5 files changed

+112
-39
lines changed

5 files changed

+112
-39
lines changed

src/librustc/lint/context.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use self::TargetLint::*;
2929
use std::slice;
3030
use rustc_data_structures::sync::{RwLock, ReadGuard};
3131
use lint::{EarlyLintPassObject, LateLintPassObject};
32-
use lint::{self, Level, Lint, LintId, LintPass, LintBuffer};
32+
use lint::{Level, Lint, LintId, LintPass, LintBuffer};
3333
use lint::builtin::BuiltinLintDiagnostics;
3434
use lint::levels::{LintLevelSets, LintLevelsBuilder};
3535
use middle::privacy::AccessLevels;
@@ -468,14 +468,7 @@ pub trait LintContext<'tcx>: Sized {
468468

469469
/// Emit a lint at the appropriate level, for a particular span.
470470
fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) {
471-
match self.lints().future_incompatible(LintId::of(lint)) {
472-
Some(_) => self.lookup_and_emit(lint, Some(span), msg),
473-
None => {
474-
if !lint::in_external_macro(lint, span) {
475-
self.lookup_and_emit(lint, Some(span), msg);
476-
}
477-
}
478-
}
471+
self.lookup_and_emit(lint, Some(span), msg);
479472
}
480473

481474
fn struct_span_lint<S: Into<MultiSpan>>(&self,

src/librustc/lint/mod.rs

+42-26
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use lint::builtin::BuiltinLintDiagnostics;
4141
use session::{Session, DiagnosticMessageId};
4242
use std::hash;
4343
use syntax::ast;
44-
use syntax::codemap::MultiSpan;
44+
use syntax::codemap::{MultiSpan, ExpnFormat};
4545
use syntax::edition::Edition;
4646
use syntax::symbol::Symbol;
4747
use 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)]
6159
pub 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
}

src/test/ui/in-band-lifetimes/ellided-lifetimes-macro-checks.rs renamed to src/test/ui/lint/auxiliary/lints-in-foreign-macros.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,18 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10-
#![feature(nll)]
11-
#![deny(elided_lifetime_in_path)]
1210

13-
fn main() {
14-
format!("foo {}", 22)
11+
#[macro_export]
12+
macro_rules! bar {
13+
() => {use std::string::ToString;}
14+
}
15+
16+
#[macro_export]
17+
macro_rules! baz {
18+
($i:item) => ($i)
19+
}
20+
21+
#[macro_export]
22+
macro_rules! baz2 {
23+
($($i:tt)*) => ($($i)*)
1524
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:lints-in-foreign-macros.rs
12+
// compile-pass
13+
14+
#![warn(unused_imports)]
15+
16+
#[macro_use]
17+
extern crate lints_in_foreign_macros;
18+
19+
macro_rules! foo {
20+
() => {use std::string::ToString;} //~ WARN: unused import
21+
}
22+
23+
mod a { foo!(); }
24+
mod b { bar!(); }
25+
mod c { baz!(use std::string::ToString;); } //~ WARN: unused import
26+
mod d { baz2!(use std::string::ToString;); } //~ WARN: unused import
27+
28+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
warning: unused import: `std::string::ToString`
2+
--> $DIR/lints-in-foreign-macros.rs:20:16
3+
|
4+
LL | () => {use std::string::ToString;} //~ WARN: unused import
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
...
7+
LL | mod a { foo!(); }
8+
| ------- in this macro invocation
9+
|
10+
note: lint level defined here
11+
--> $DIR/lints-in-foreign-macros.rs:14:9
12+
|
13+
LL | #![warn(unused_imports)]
14+
| ^^^^^^^^^^^^^^
15+
16+
warning: unused import: `std::string::ToString`
17+
--> $DIR/lints-in-foreign-macros.rs:25:18
18+
|
19+
LL | mod c { baz!(use std::string::ToString;); } //~ WARN: unused import
20+
| ^^^^^^^^^^^^^^^^^^^^^
21+
22+
warning: unused import: `std::string::ToString`
23+
--> $DIR/lints-in-foreign-macros.rs:26:19
24+
|
25+
LL | mod d { baz2!(use std::string::ToString;); } //~ WARN: unused import
26+
| ^^^^^^^^^^^^^^^^^^^^^
27+

0 commit comments

Comments
 (0)