diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 4ee5d86b8f8e..610ed681827e 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -27,7 +27,7 @@ use self::TargetLint::*; use std::slice; -use rustc_data_structures::sync::{RwLock, ReadGuard}; +use rustc_data_structures::sync::ReadGuard; use lint::{EarlyLintPassObject, LateLintPassObject}; use lint::{Level, Lint, LintId, LintPass, LintBuffer}; use lint::builtin::BuiltinLintDiagnostics; @@ -59,8 +59,8 @@ pub struct LintStore { lints: Vec<(&'static Lint, bool)>, /// Trait objects for each lint pass. - /// This is only `None` while performing a lint pass. See the definition - /// of `LintSession::new`. + /// This is only `None` while performing a lint pass. + pre_expansion_passes: Option>, early_passes: Option>, late_passes: Option>, @@ -139,6 +139,7 @@ impl LintStore { pub fn new() -> LintStore { LintStore { lints: vec![], + pre_expansion_passes: Some(vec![]), early_passes: Some(vec![]), late_passes: Some(vec![]), by_name: FxHashMap(), @@ -165,6 +166,15 @@ impl LintStore { self.early_passes.as_mut().unwrap().push(pass); } + pub fn register_pre_expansion_pass( + &mut self, + sess: Option<&Session>, + pass: EarlyLintPassObject, + ) { + self.push_pass(sess, false, &pass); + self.pre_expansion_passes.as_mut().unwrap().push(pass); + } + pub fn register_late_pass(&mut self, sess: Option<&Session>, from_plugin: bool, @@ -332,28 +342,6 @@ impl LintStore { } } -impl<'a, PassObject: LintPassObject> LintSession<'a, PassObject> { - /// Creates a new `LintSession`, by moving out the `LintStore`'s initial - /// lint levels and pass objects. These can be restored using the `restore` - /// method. - fn new(store: &'a RwLock) -> LintSession<'a, PassObject> { - let mut s = store.borrow_mut(); - let passes = PassObject::take_passes(&mut *s); - drop(s); - LintSession { - lints: store.borrow(), - passes, - } - } - - /// Restores the levels back to the original lint store. - fn restore(self, store: &RwLock) { - drop(self.lints); - let mut s = store.borrow_mut(); - PassObject::restore_passes(&mut *s, self.passes); - } -} - /// Context for lint checking after type checking. pub struct LateContext<'a, 'tcx: 'a> { /// Type context we're checking in. @@ -405,30 +393,11 @@ macro_rules! run_lints { ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.lint_sess_mut().passes = Some(passes); }) } -pub trait LintPassObject: Sized { - fn take_passes(store: &mut LintStore) -> Option>; - fn restore_passes(store: &mut LintStore, passes: Option>); -} +pub trait LintPassObject: Sized {} -impl LintPassObject for EarlyLintPassObject { - fn take_passes(store: &mut LintStore) -> Option> { - store.early_passes.take() - } +impl LintPassObject for EarlyLintPassObject {} - fn restore_passes(store: &mut LintStore, passes: Option>) { - store.early_passes = passes; - } -} - -impl LintPassObject for LateLintPassObject { - fn take_passes(store: &mut LintStore) -> Option> { - store.late_passes.take() - } - - fn restore_passes(store: &mut LintStore, passes: Option>) { - store.late_passes = passes; - } -} +impl LintPassObject for LateLintPassObject {} pub trait LintContext<'tcx>: Sized { @@ -515,14 +484,21 @@ pub trait LintContext<'tcx>: Sized { impl<'a> EarlyContext<'a> { - fn new(sess: &'a Session, - krate: &'a ast::Crate) -> EarlyContext<'a> { + fn new( + sess: &'a Session, + krate: &'a ast::Crate, + passes: Option>, + buffered: LintBuffer, + ) -> EarlyContext<'a> { EarlyContext { sess, krate, - lint_sess: LintSession::new(&sess.lint_store), + lint_sess: LintSession { + lints: sess.lint_store.borrow(), + passes, + }, builder: LintLevelSets::builder(sess), - buffered: sess.buffered_lints.borrow_mut().take().unwrap(), + buffered, } } @@ -1041,9 +1017,14 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> { run_lints!(self, check_attribute, attr); } - fn visit_mac_def(&mut self, _mac: &'a ast::MacroDef, id: ast::NodeId) { + fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { + run_lints!(self, check_mac_def, mac, id); self.check_id(id); } + + fn visit_mac(&mut self, mac: &'ast ast::Mac) { + run_lints!(self, check_mac, mac); + } } @@ -1054,48 +1035,77 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); let krate = tcx.hir.krate(); + let passes = tcx.sess.lint_store.borrow_mut().late_passes.take(); - let mut cx = LateContext { - tcx, - tables: &ty::TypeckTables::empty(None), - param_env: ty::ParamEnv::empty(), - access_levels, - lint_sess: LintSession::new(&tcx.sess.lint_store), - last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID, - generics: None, + let passes = { + let mut cx = LateContext { + tcx, + tables: &ty::TypeckTables::empty(None), + param_env: ty::ParamEnv::empty(), + access_levels, + lint_sess: LintSession { + passes, + lints: tcx.sess.lint_store.borrow(), + }, + last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID, + generics: None, + }; + + // Visit the whole crate. + cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| { + // since the root module isn't visited as an item (because it isn't an + // item), warn for it here. + run_lints!(cx, check_crate, krate); + + hir_visit::walk_crate(cx, krate); + + run_lints!(cx, check_crate_post, krate); + }); + cx.lint_sess.passes }; - // Visit the whole crate. - cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| { - // since the root module isn't visited as an item (because it isn't an - // item), warn for it here. - run_lints!(cx, check_crate, krate); - - hir_visit::walk_crate(cx, krate); - - run_lints!(cx, check_crate_post, krate); - }); - // Put the lint store levels and passes back in the session. - cx.lint_sess.restore(&tcx.sess.lint_store); + tcx.sess.lint_store.borrow_mut().late_passes = passes; } -pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) { - let mut cx = EarlyContext::new(sess, krate); +pub fn check_ast_crate( + sess: &Session, + krate: &ast::Crate, + pre_expansion: bool, +) { + let (passes, buffered) = if pre_expansion { + ( + sess.lint_store.borrow_mut().pre_expansion_passes.take(), + LintBuffer::new(), + ) + } else { + ( + sess.lint_store.borrow_mut().early_passes.take(), + sess.buffered_lints.borrow_mut().take().unwrap(), + ) + }; + let (passes, buffered) = { + let mut cx = EarlyContext::new(sess, krate, passes, buffered); - // Visit the whole crate. - cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| { - // since the root module isn't visited as an item (because it isn't an - // item), warn for it here. - run_lints!(cx, check_crate, krate); + // Visit the whole crate. + cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| { + // since the root module isn't visited as an item (because it isn't an + // item), warn for it here. + run_lints!(cx, check_crate, krate); - ast_visit::walk_crate(cx, krate); + ast_visit::walk_crate(cx, krate); - run_lints!(cx, check_crate_post, krate); - }); + run_lints!(cx, check_crate_post, krate); + }); + (cx.lint_sess.passes, cx.buffered) + }; // Put the lint store levels and passes back in the session. - cx.lint_sess.restore(&sess.lint_store); + if pre_expansion { + sess.lint_store.borrow_mut().pre_expansion_passes = passes; + } else { + sess.lint_store.borrow_mut().early_passes = passes; + } // All of the buffered lints should have been emitted at this point. // If not, that means that we somehow buffered a lint for a node id @@ -1107,7 +1117,7 @@ pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) { // unused_macro lint) anymore. So we only run this check // when we're not in rustdoc mode. (see issue #47639) if !sess.opts.actually_rustdoc { - for (_id, lints) in cx.buffered.map { + for (_id, lints) in buffered.map { for early_lint in lints { sess.delay_span_bug(early_lint.span, "failed to process buffered lint here"); } diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index bff596e21e53..e3d35a7c105e 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -327,6 +327,8 @@ pub trait EarlyLintPass: LintPass { fn check_lifetime(&mut self, _: &EarlyContext, _: &ast::Lifetime) { } fn check_path(&mut self, _: &EarlyContext, _: &ast::Path, _: ast::NodeId) { } fn check_attribute(&mut self, _: &EarlyContext, _: &ast::Attribute) { } + fn check_mac_def(&mut self, _: &EarlyContext, _: &ast::MacroDef, _id: ast::NodeId) { } + fn check_mac(&mut self, _: &EarlyContext, _: &ast::Mac) { } /// Called when entering a syntax node that can have lint attributes such /// as `#[allow(...)]`. Called with *all* the attributes of that node. @@ -341,6 +343,8 @@ pub type EarlyLintPassObject = Box LateLintPass<'a, 'tcx> + sync::Send + sync::Sync + 'static>; + + /// Identifies a lint known to the compiler. #[derive(Clone, Copy, Debug)] pub struct LintId { diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 96e9616699d3..349e00bfd90d 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -922,6 +922,10 @@ where return Err(CompileIncomplete::Stopped); } + time(sess, "pre ast expansion lint checks", || { + lint::check_ast_crate(sess, &krate, true) + }); + let mut resolver = Resolver::new( sess, cstore, @@ -1134,7 +1138,7 @@ where }); time(sess, "early lint checks", || { - lint::check_ast_crate(sess, &krate) + lint::check_ast_crate(sess, &krate, false) }); // Discard hygiene data, which isn't required after lowering to HIR. diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index d6e5c70b8f7e..6950166a5cd9 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -41,6 +41,7 @@ use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext}; use std::collections::HashSet; +use syntax::tokenstream::{TokenTree, TokenStream}; use syntax::ast; use syntax::attr; use syntax::codemap::Spanned; @@ -1784,3 +1785,70 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestFunctions { }; } } + +declare_lint! { + pub ASYNC_IDENTS, + Allow, + "detects `async` being used as an identifier" +} + +/// Checks for uses of `async` as an identifier +#[derive(Clone)] +pub struct Async2018; + +impl LintPass for Async2018 { + fn get_lints(&self) -> LintArray { + lint_array!() + } +} + +impl Async2018 { + fn check_tokens(&mut self, cx: &EarlyContext, tokens: TokenStream) { + for tt in tokens.into_trees() { + match tt { + TokenTree::Token(span, tok) => match tok.ident() { + // only report non-raw idents + Some((ident, false)) if ident.as_str() == "async" => { + self.report(cx, span.substitute_dummy(ident.span)) + }, + _ => {}, + } + TokenTree::Delimited(_, ref delim) => { + self.check_tokens(cx, delim.tts.clone().into()) + }, + } + } + } + fn report(&mut self, cx: &EarlyContext, span: Span) { + // don't lint `r#async` + if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&span) { + return; + } + let mut lint = cx.struct_span_lint( + ASYNC_IDENTS, + span, + "`async` is a keyword in the 2018 edition", + ); + lint.span_suggestion_with_applicability( + span, + "you can use a raw identifier to stay compatible", + "r#async".to_string(), + Applicability::MachineApplicable, + ); + lint.emit() + } +} + +impl EarlyLintPass for Async2018 { + fn check_mac_def(&mut self, cx: &EarlyContext, mac_def: &ast::MacroDef, _id: ast::NodeId) { + self.check_tokens(cx, mac_def.stream()); + } + fn check_mac(&mut self, cx: &EarlyContext, mac: &ast::Mac) { + self.check_tokens(cx, mac.node.tts.clone().into()); + } + fn check_ident(&mut self, cx: &EarlyContext, ident: ast::Ident) { + if ident.as_str() == "async" { + self.report(cx, ident.span); + } + } +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 359b056b5a2d..399725c0023f 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -80,6 +80,14 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { ) } + macro_rules! add_pre_expansion_builtin { + ($sess:ident, $($name:ident),*,) => ( + {$( + store.register_early_pass($sess, false, box $name); + )*} + ) + } + macro_rules! add_early_builtin_with_new { ($sess:ident, $($name:ident),*,) => ( {$( @@ -94,6 +102,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { ) } + add_pre_expansion_builtin!(sess, + Async2018, + ); + add_early_builtin!(sess, UnusedParens, UnusedImportBraces, @@ -177,6 +189,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { add_lint_group!(sess, "rust_2018_idioms", BARE_TRAIT_OBJECTS, + ASYNC_IDENTS, UNREACHABLE_PUB, UNUSED_EXTERN_CRATES, ELLIPSIS_INCLUSIVE_RANGE_PATTERNS); diff --git a/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr b/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr index c03ee7f983f5..56d4a86096f6 100644 --- a/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr +++ b/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr @@ -1,3 +1,12 @@ +error[E0453]: allow(test_lint) overruled by outer forbid(test_lint) + --> $DIR/lint-plugin-forbid-attrs.rs:20:9 + | +LL | #![forbid(test_lint)] + | --------- `forbid` level set here +... +LL | #[allow(test_lint)] + | ^^^^^^^^^ overruled by previous forbid + error: item is named 'lintme' --> $DIR/lint-plugin-forbid-attrs.rs:18:1 | @@ -10,15 +19,6 @@ note: lint level defined here LL | #![forbid(test_lint)] | ^^^^^^^^^ -error[E0453]: allow(test_lint) overruled by outer forbid(test_lint) - --> $DIR/lint-plugin-forbid-attrs.rs:20:9 - | -LL | #![forbid(test_lint)] - | --------- `forbid` level set here -... -LL | #[allow(test_lint)] - | ^^^^^^^^^ overruled by previous forbid - error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0453`. diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr index f351a9e69d01..2dbe98310146 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr @@ -1,17 +1,3 @@ -warning: macro_escape is a deprecated synonym for macro_use - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:500:1 - | -LL | #[macro_escape] - | ^^^^^^^^^^^^^^^ - -warning: macro_escape is a deprecated synonym for macro_use - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:503:17 - | -LL | mod inner { #![macro_escape] } - | ^^^^^^^^^^^^^^^^ - | - = help: consider an outer attribute, #[macro_use] mod ... - warning: unknown lint: `x5400` --> $DIR/issue-43106-gating-of-builtin-attrs.rs:49:33 | @@ -186,6 +172,20 @@ warning: unknown lint: `x5100` LL | #[deny(x5100)] impl S { } | ^^^^^ +warning: macro_escape is a deprecated synonym for macro_use + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:500:1 + | +LL | #[macro_escape] + | ^^^^^^^^^^^^^^^ + +warning: macro_escape is a deprecated synonym for macro_use + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:503:17 + | +LL | mod inner { #![macro_escape] } + | ^^^^^^^^^^^^^^^^ + | + = help: consider an outer attribute, #[macro_use] mod ... + warning: `repr` attribute isn't configurable with a literal --> $DIR/issue-43106-gating-of-builtin-attrs.rs:316:17 | diff --git a/src/test/ui/rust-2018/async-ident.fixed b/src/test/ui/rust-2018/async-ident.fixed new file mode 100644 index 000000000000..8ede6c07bf8b --- /dev/null +++ b/src/test/ui/rust-2018/async-ident.fixed @@ -0,0 +1,32 @@ +// Copyright 2016 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. + +#![feature(raw_identifiers)] +#![deny(rust_2018_idioms)] +#![allow(dead_code)] + +// run-rustfix + +fn r#async() {} //~ ERROR async + +macro_rules! foo { + ($foo:ident) => {}; + ($r#async:expr, r#async) => {}; + //~^ ERROR async + //~| ERROR async +} + +foo!(async); + +mod dont_lint_raw { + fn r#async() {} +} + +fn main() {} diff --git a/src/test/ui/rust-2018/async-ident.rs b/src/test/ui/rust-2018/async-ident.rs new file mode 100644 index 000000000000..6e529ec0229d --- /dev/null +++ b/src/test/ui/rust-2018/async-ident.rs @@ -0,0 +1,32 @@ +// Copyright 2016 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. + +#![feature(raw_identifiers)] +#![deny(rust_2018_idioms)] +#![allow(dead_code)] + +// run-rustfix + +fn async() {} //~ ERROR async + +macro_rules! foo { + ($foo:ident) => {}; + ($async:expr, async) => {}; + //~^ ERROR async + //~| ERROR async +} + +foo!(async); + +mod dont_lint_raw { + fn r#async() {} +} + +fn main() {} diff --git a/src/test/ui/rust-2018/async-ident.stderr b/src/test/ui/rust-2018/async-ident.stderr new file mode 100644 index 000000000000..22a3e1aedcc3 --- /dev/null +++ b/src/test/ui/rust-2018/async-ident.stderr @@ -0,0 +1,27 @@ +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:17:4 + | +LL | fn async() {} //~ ERROR async + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | +note: lint level defined here + --> $DIR/async-ident.rs:12:9 + | +LL | #![deny(rust_2018_idioms)] + | ^^^^^^^^^^^^^^^^ + = note: #[deny(async_idents)] implied by #[deny(rust_2018_idioms)] + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:21:7 + | +LL | ($async:expr, async) => {}; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:21:19 + | +LL | ($async:expr, async) => {}; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + +error: aborting due to 3 previous errors +