diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index c9874f510f731..610ed681827ea 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. @@ -395,7 +383,7 @@ pub struct EarlyContext<'a> { } /// Convenience macro for calling a `LintPass` method on every pass in the context. -macro_rules! run_lints { ($cx:expr, $f:ident, $ps:ident, $($args:expr),*) => ({ +macro_rules! run_lints { ($cx:expr, $f:ident, $($args:expr),*) => ({ // Move the vector of passes out of `$cx` so that we can // iterate over it mutably while passing `$cx` to the methods. let mut passes = $cx.lint_sess_mut().passes.take().unwrap(); @@ -405,30 +393,11 @@ macro_rules! run_lints { ($cx:expr, $f:ident, $ps: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>); -} - -impl LintPassObject for EarlyLintPassObject { - fn take_passes(store: &mut LintStore) -> Option> { - store.early_passes.take() - } +pub trait LintPassObject: Sized {} - 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() - } +impl LintPassObject for EarlyLintPassObject {} - 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, } } @@ -558,12 +534,12 @@ impl<'a, 'tcx> LintContext<'tcx> for LateContext<'a, 'tcx> { fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute]) { debug!("late context: enter_attrs({:?})", attrs); - run_lints!(self, enter_lint_attrs, late_passes, attrs); + run_lints!(self, enter_lint_attrs, attrs); } fn exit_attrs(&mut self, attrs: &'tcx [ast::Attribute]) { debug!("late context: exit_attrs({:?})", attrs); - run_lints!(self, exit_lint_attrs, late_passes, attrs); + run_lints!(self, exit_lint_attrs, attrs); } fn lookup>(&self, @@ -615,12 +591,12 @@ impl<'a> LintContext<'a> for EarlyContext<'a> { fn enter_attrs(&mut self, attrs: &'a [ast::Attribute]) { debug!("early context: enter_attrs({:?})", attrs); - run_lints!(self, enter_lint_attrs, early_passes, attrs); + run_lints!(self, enter_lint_attrs, attrs); } fn exit_attrs(&mut self, attrs: &'a [ast::Attribute]) { debug!("early context: exit_attrs({:?})", attrs); - run_lints!(self, exit_lint_attrs, early_passes, attrs); + run_lints!(self, exit_lint_attrs, attrs); } fn lookup>(&self, @@ -686,9 +662,9 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { } fn visit_body(&mut self, body: &'tcx hir::Body) { - run_lints!(self, check_body, late_passes, body); + run_lints!(self, check_body, body); hir_visit::walk_body(self, body); - run_lints!(self, check_body_post, late_passes, body); + run_lints!(self, check_body_post, body); } fn visit_item(&mut self, it: &'tcx hir::Item) { @@ -696,9 +672,9 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { self.generics = it.node.generics(); self.with_lint_attrs(it.id, &it.attrs, |cx| { cx.with_param_env(it.id, |cx| { - run_lints!(cx, check_item, late_passes, it); + run_lints!(cx, check_item, it); hir_visit::walk_item(cx, it); - run_lints!(cx, check_item_post, late_passes, it); + run_lints!(cx, check_item_post, it); }); }); self.generics = generics; @@ -707,23 +683,23 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem) { self.with_lint_attrs(it.id, &it.attrs, |cx| { cx.with_param_env(it.id, |cx| { - run_lints!(cx, check_foreign_item, late_passes, it); + run_lints!(cx, check_foreign_item, it); hir_visit::walk_foreign_item(cx, it); - run_lints!(cx, check_foreign_item_post, late_passes, it); + run_lints!(cx, check_foreign_item_post, it); }); }) } fn visit_pat(&mut self, p: &'tcx hir::Pat) { - run_lints!(self, check_pat, late_passes, p); + run_lints!(self, check_pat, p); hir_visit::walk_pat(self, p); } fn visit_expr(&mut self, e: &'tcx hir::Expr) { self.with_lint_attrs(e.id, &e.attrs, |cx| { - run_lints!(cx, check_expr, late_passes, e); + run_lints!(cx, check_expr, e); hir_visit::walk_expr(cx, e); - run_lints!(cx, check_expr_post, late_passes, e); + run_lints!(cx, check_expr_post, e); }) } @@ -733,7 +709,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { // - local // - expression // so we keep track of lint levels there - run_lints!(self, check_stmt, late_passes, s); + run_lints!(self, check_stmt, s); hir_visit::walk_stmt(self, s); } @@ -744,9 +720,9 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { let old_tables = self.tables; self.tables = self.tcx.body_tables(body_id); let body = self.tcx.hir.body(body_id); - run_lints!(self, check_fn, late_passes, fk, decl, body, span, id); + run_lints!(self, check_fn, fk, decl, body, span, id); hir_visit::walk_fn(self, fk, decl, body_id, span, id); - run_lints!(self, check_fn_post, late_passes, fk, decl, body, span, id); + run_lints!(self, check_fn_post, fk, decl, body, span, id); self.tables = old_tables; } @@ -756,14 +732,14 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { g: &'tcx hir::Generics, item_id: ast::NodeId, _: Span) { - run_lints!(self, check_struct_def, late_passes, s, name, g, item_id); + run_lints!(self, check_struct_def, s, name, g, item_id); hir_visit::walk_struct_def(self, s); - run_lints!(self, check_struct_def_post, late_passes, s, name, g, item_id); + run_lints!(self, check_struct_def_post, s, name, g, item_id); } fn visit_struct_field(&mut self, s: &'tcx hir::StructField) { self.with_lint_attrs(s.id, &s.attrs, |cx| { - run_lints!(cx, check_struct_field, late_passes, s); + run_lints!(cx, check_struct_field, s); hir_visit::walk_struct_field(cx, s); }) } @@ -773,68 +749,68 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { g: &'tcx hir::Generics, item_id: ast::NodeId) { self.with_lint_attrs(v.node.data.id(), &v.node.attrs, |cx| { - run_lints!(cx, check_variant, late_passes, v, g); + run_lints!(cx, check_variant, v, g); hir_visit::walk_variant(cx, v, g, item_id); - run_lints!(cx, check_variant_post, late_passes, v, g); + run_lints!(cx, check_variant_post, v, g); }) } fn visit_ty(&mut self, t: &'tcx hir::Ty) { - run_lints!(self, check_ty, late_passes, t); + run_lints!(self, check_ty, t); hir_visit::walk_ty(self, t); } fn visit_name(&mut self, sp: Span, name: ast::Name) { - run_lints!(self, check_name, late_passes, sp, name); + run_lints!(self, check_name, sp, name); } fn visit_mod(&mut self, m: &'tcx hir::Mod, s: Span, n: ast::NodeId) { - run_lints!(self, check_mod, late_passes, m, s, n); + run_lints!(self, check_mod, m, s, n); hir_visit::walk_mod(self, m, n); - run_lints!(self, check_mod_post, late_passes, m, s, n); + run_lints!(self, check_mod_post, m, s, n); } fn visit_local(&mut self, l: &'tcx hir::Local) { self.with_lint_attrs(l.id, &l.attrs, |cx| { - run_lints!(cx, check_local, late_passes, l); + run_lints!(cx, check_local, l); hir_visit::walk_local(cx, l); }) } fn visit_block(&mut self, b: &'tcx hir::Block) { - run_lints!(self, check_block, late_passes, b); + run_lints!(self, check_block, b); hir_visit::walk_block(self, b); - run_lints!(self, check_block_post, late_passes, b); + run_lints!(self, check_block_post, b); } fn visit_arm(&mut self, a: &'tcx hir::Arm) { - run_lints!(self, check_arm, late_passes, a); + run_lints!(self, check_arm, a); hir_visit::walk_arm(self, a); } fn visit_decl(&mut self, d: &'tcx hir::Decl) { - run_lints!(self, check_decl, late_passes, d); + run_lints!(self, check_decl, d); hir_visit::walk_decl(self, d); } fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam) { - run_lints!(self, check_generic_param, late_passes, p); + run_lints!(self, check_generic_param, p); hir_visit::walk_generic_param(self, p); } fn visit_generics(&mut self, g: &'tcx hir::Generics) { - run_lints!(self, check_generics, late_passes, g); + run_lints!(self, check_generics, g); hir_visit::walk_generics(self, g); } fn visit_where_predicate(&mut self, p: &'tcx hir::WherePredicate) { - run_lints!(self, check_where_predicate, late_passes, p); + run_lints!(self, check_where_predicate, p); hir_visit::walk_where_predicate(self, p); } fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef, m: hir::TraitBoundModifier) { - run_lints!(self, check_poly_trait_ref, late_passes, t, m); + run_lints!(self, check_poly_trait_ref, t, m); hir_visit::walk_poly_trait_ref(self, t, m); } @@ -843,9 +819,9 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { self.generics = Some(&trait_item.generics); self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| { cx.with_param_env(trait_item.id, |cx| { - run_lints!(cx, check_trait_item, late_passes, trait_item); + run_lints!(cx, check_trait_item, trait_item); hir_visit::walk_trait_item(cx, trait_item); - run_lints!(cx, check_trait_item_post, late_passes, trait_item); + run_lints!(cx, check_trait_item_post, trait_item); }); }); self.generics = generics; @@ -856,71 +832,71 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { self.generics = Some(&impl_item.generics); self.with_lint_attrs(impl_item.id, &impl_item.attrs, |cx| { cx.with_param_env(impl_item.id, |cx| { - run_lints!(cx, check_impl_item, late_passes, impl_item); + run_lints!(cx, check_impl_item, impl_item); hir_visit::walk_impl_item(cx, impl_item); - run_lints!(cx, check_impl_item_post, late_passes, impl_item); + run_lints!(cx, check_impl_item_post, impl_item); }); }); self.generics = generics; } fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { - run_lints!(self, check_lifetime, late_passes, lt); + run_lints!(self, check_lifetime, lt); hir_visit::walk_lifetime(self, lt); } fn visit_path(&mut self, p: &'tcx hir::Path, id: ast::NodeId) { - run_lints!(self, check_path, late_passes, p, id); + run_lints!(self, check_path, p, id); hir_visit::walk_path(self, p); } fn visit_attribute(&mut self, attr: &'tcx ast::Attribute) { - run_lints!(self, check_attribute, late_passes, attr); + run_lints!(self, check_attribute, attr); } } impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> { fn visit_item(&mut self, it: &'a ast::Item) { self.with_lint_attrs(it.id, &it.attrs, |cx| { - run_lints!(cx, check_item, early_passes, it); + run_lints!(cx, check_item, it); ast_visit::walk_item(cx, it); - run_lints!(cx, check_item_post, early_passes, it); + run_lints!(cx, check_item_post, it); }) } fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) { self.with_lint_attrs(it.id, &it.attrs, |cx| { - run_lints!(cx, check_foreign_item, early_passes, it); + run_lints!(cx, check_foreign_item, it); ast_visit::walk_foreign_item(cx, it); - run_lints!(cx, check_foreign_item_post, early_passes, it); + run_lints!(cx, check_foreign_item_post, it); }) } fn visit_pat(&mut self, p: &'a ast::Pat) { - run_lints!(self, check_pat, early_passes, p); + run_lints!(self, check_pat, p); self.check_id(p.id); ast_visit::walk_pat(self, p); } fn visit_expr(&mut self, e: &'a ast::Expr) { self.with_lint_attrs(e.id, &e.attrs, |cx| { - run_lints!(cx, check_expr, early_passes, e); + run_lints!(cx, check_expr, e); ast_visit::walk_expr(cx, e); }) } fn visit_stmt(&mut self, s: &'a ast::Stmt) { - run_lints!(self, check_stmt, early_passes, s); + run_lints!(self, check_stmt, s); self.check_id(s.id); ast_visit::walk_stmt(self, s); } fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, decl: &'a ast::FnDecl, span: Span, id: ast::NodeId) { - run_lints!(self, check_fn, early_passes, fk, decl, span, id); + run_lints!(self, check_fn, fk, decl, span, id); self.check_id(id); ast_visit::walk_fn(self, fk, decl, span); - run_lints!(self, check_fn_post, early_passes, fk, decl, span, id); + run_lints!(self, check_fn_post, fk, decl, span, id); } fn visit_variant_data(&mut self, @@ -929,121 +905,126 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> { g: &'a ast::Generics, item_id: ast::NodeId, _: Span) { - run_lints!(self, check_struct_def, early_passes, s, ident, g, item_id); + run_lints!(self, check_struct_def, s, ident, g, item_id); self.check_id(s.id()); ast_visit::walk_struct_def(self, s); - run_lints!(self, check_struct_def_post, early_passes, s, ident, g, item_id); + run_lints!(self, check_struct_def_post, s, ident, g, item_id); } fn visit_struct_field(&mut self, s: &'a ast::StructField) { self.with_lint_attrs(s.id, &s.attrs, |cx| { - run_lints!(cx, check_struct_field, early_passes, s); + run_lints!(cx, check_struct_field, s); ast_visit::walk_struct_field(cx, s); }) } fn visit_variant(&mut self, v: &'a ast::Variant, g: &'a ast::Generics, item_id: ast::NodeId) { self.with_lint_attrs(item_id, &v.node.attrs, |cx| { - run_lints!(cx, check_variant, early_passes, v, g); + run_lints!(cx, check_variant, v, g); ast_visit::walk_variant(cx, v, g, item_id); - run_lints!(cx, check_variant_post, early_passes, v, g); + run_lints!(cx, check_variant_post, v, g); }) } fn visit_ty(&mut self, t: &'a ast::Ty) { - run_lints!(self, check_ty, early_passes, t); + run_lints!(self, check_ty, t); self.check_id(t.id); ast_visit::walk_ty(self, t); } fn visit_ident(&mut self, ident: ast::Ident) { - run_lints!(self, check_ident, early_passes, ident); + run_lints!(self, check_ident, ident); } fn visit_mod(&mut self, m: &'a ast::Mod, s: Span, _a: &[ast::Attribute], n: ast::NodeId) { - run_lints!(self, check_mod, early_passes, m, s, n); + run_lints!(self, check_mod, m, s, n); self.check_id(n); ast_visit::walk_mod(self, m); - run_lints!(self, check_mod_post, early_passes, m, s, n); + run_lints!(self, check_mod_post, m, s, n); } fn visit_local(&mut self, l: &'a ast::Local) { self.with_lint_attrs(l.id, &l.attrs, |cx| { - run_lints!(cx, check_local, early_passes, l); + run_lints!(cx, check_local, l); ast_visit::walk_local(cx, l); }) } fn visit_block(&mut self, b: &'a ast::Block) { - run_lints!(self, check_block, early_passes, b); + run_lints!(self, check_block, b); self.check_id(b.id); ast_visit::walk_block(self, b); - run_lints!(self, check_block_post, early_passes, b); + run_lints!(self, check_block_post, b); } fn visit_arm(&mut self, a: &'a ast::Arm) { - run_lints!(self, check_arm, early_passes, a); + run_lints!(self, check_arm, a); ast_visit::walk_arm(self, a); } fn visit_expr_post(&mut self, e: &'a ast::Expr) { - run_lints!(self, check_expr_post, early_passes, e); + run_lints!(self, check_expr_post, e); } fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { - run_lints!(self, check_generic_param, early_passes, param); + run_lints!(self, check_generic_param, param); ast_visit::walk_generic_param(self, param); } fn visit_generics(&mut self, g: &'a ast::Generics) { - run_lints!(self, check_generics, early_passes, g); + run_lints!(self, check_generics, g); ast_visit::walk_generics(self, g); } fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) { - run_lints!(self, check_where_predicate, early_passes, p); + run_lints!(self, check_where_predicate, p); ast_visit::walk_where_predicate(self, p); } fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef, m: &'a ast::TraitBoundModifier) { - run_lints!(self, check_poly_trait_ref, early_passes, t, m); + run_lints!(self, check_poly_trait_ref, t, m); ast_visit::walk_poly_trait_ref(self, t, m); } fn visit_trait_item(&mut self, trait_item: &'a ast::TraitItem) { self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| { - run_lints!(cx, check_trait_item, early_passes, trait_item); + run_lints!(cx, check_trait_item, trait_item); ast_visit::walk_trait_item(cx, trait_item); - run_lints!(cx, check_trait_item_post, early_passes, trait_item); + run_lints!(cx, check_trait_item_post, trait_item); }); } fn visit_impl_item(&mut self, impl_item: &'a ast::ImplItem) { self.with_lint_attrs(impl_item.id, &impl_item.attrs, |cx| { - run_lints!(cx, check_impl_item, early_passes, impl_item); + run_lints!(cx, check_impl_item, impl_item); ast_visit::walk_impl_item(cx, impl_item); - run_lints!(cx, check_impl_item_post, early_passes, impl_item); + run_lints!(cx, check_impl_item_post, impl_item); }); } fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) { - run_lints!(self, check_lifetime, early_passes, lt); + run_lints!(self, check_lifetime, lt); self.check_id(lt.id); } fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) { - run_lints!(self, check_path, early_passes, p, id); + run_lints!(self, check_path, p, id); self.check_id(id); ast_visit::walk_path(self, p); } fn visit_attribute(&mut self, attr: &'a ast::Attribute) { - run_lints!(self, check_attribute, early_passes, attr); + 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 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, + }; - 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, - }; - - // 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, late_passes, 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); - hir_visit::walk_crate(cx, krate); + hir_visit::walk_crate(cx, krate); - run_lints!(cx, check_crate_post, late_passes, krate); - }); + run_lints!(cx, check_crate_post, krate); + }); + cx.lint_sess.passes + }; // 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, early_passes, 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, early_passes, 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 bff596e21e53a..e3d35a7c105ea 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 96e9616699d37..349e00bfd90d4 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 d6e5c70b8f7e1..ecfb52f136a62 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!(ASYNC_IDENTS) + } +} + +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 359b056b5a2d1..bb0f0e7832fd2 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, @@ -211,6 +223,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { reference: "issue #35896 ", edition: Some(Edition::Edition2018), }, + FutureIncompatibleInfo { + id: LintId::of(ASYNC_IDENTS), + reference: "issue #49716 ", + edition: Some(Edition::Edition2018), + }, FutureIncompatibleInfo { id: LintId::of(SAFE_EXTERN_STATICS), reference: "issue #36247 ", diff --git a/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr b/src/test/ui-fulldeps/lint-plugin-forbid-attrs.stderr index c03ee7f983f53..56d4a86096f68 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/auxiliary/edition-kw-macro-2015.rs b/src/test/ui/auxiliary/edition-kw-macro-2015.rs index 69952e9f90af6..8f80e000e3caf 100644 --- a/src/test/ui/auxiliary/edition-kw-macro-2015.rs +++ b/src/test/ui/auxiliary/edition-kw-macro-2015.rs @@ -11,6 +11,7 @@ // edition:2015 #![feature(raw_identifiers)] +#![allow(async_idents)] #[macro_export] macro_rules! produces_async { diff --git a/src/test/ui/auxiliary/edition-kw-macro-2018.rs b/src/test/ui/auxiliary/edition-kw-macro-2018.rs index 415988586a066..d42014766ec68 100644 --- a/src/test/ui/auxiliary/edition-kw-macro-2018.rs +++ b/src/test/ui/auxiliary/edition-kw-macro-2018.rs @@ -11,6 +11,7 @@ // edition:2018 #![feature(raw_identifiers)] +#![allow(async_idents)] #[macro_export] macro_rules! produces_async { diff --git a/src/test/ui/edition-keywords-2015-2015-expansion.rs b/src/test/ui/edition-keywords-2015-2015-expansion.rs index 349ab3e27ad32..3b78ce80be208 100644 --- a/src/test/ui/edition-keywords-2015-2015-expansion.rs +++ b/src/test/ui/edition-keywords-2015-2015-expansion.rs @@ -13,6 +13,7 @@ // compile-pass #![feature(raw_identifiers)] +#![allow(async_idents)] #[macro_use] extern crate edition_kw_macro_2015; diff --git a/src/test/ui/edition-keywords-2018-2015-expansion.rs b/src/test/ui/edition-keywords-2018-2015-expansion.rs index 6e2073e0e494a..be22d8b9b0105 100644 --- a/src/test/ui/edition-keywords-2018-2015-expansion.rs +++ b/src/test/ui/edition-keywords-2018-2015-expansion.rs @@ -13,6 +13,7 @@ // compile-pass #![feature(raw_identifiers)] +#![allow(async_idents)] #[macro_use] extern crate edition_kw_macro_2015; 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 f351a9e69d011..2dbe983101466 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 0000000000000..228bf91125337 --- /dev/null +++ b/src/test/ui/rust-2018/async-ident.fixed @@ -0,0 +1,88 @@ +// 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)] +#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)] +#![deny(async_idents)] + +// edition:2015 +// run-rustfix + +fn r#async() {} //~ ERROR async +//~^ WARN hard error in the 2018 edition + +macro_rules! foo { + ($foo:ident) => {}; + ($r#async:expr, r#async) => {}; + //~^ ERROR async + //~| ERROR async + //~| WARN hard error in the 2018 edition + //~| WARN hard error in the 2018 edition +} + +foo!(async); + +mod dont_lint_raw { + fn r#async() {} +} + +mod async_trait { + trait r#async {} + //~^ ERROR async + //~| WARN hard error in the 2018 edition + struct MyStruct; + impl r#async for MyStruct {} + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} + +mod async_static { + static r#async: u32 = 0; + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} + +mod async_const { + const r#async: u32 = 0; + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} + +struct Foo; +impl Foo { fn r#async() {} } + //~^ ERROR async + //~| WARN hard error in the 2018 edition + +fn main() { + struct r#async {} + //~^ ERROR async + //~| WARN hard error in the 2018 edition + let r#async: r#async = r#async {}; + //~^ ERROR async + //~| WARN hard error in the 2018 edition + //~| ERROR async + //~| WARN hard error in the 2018 edition + //~| ERROR async + //~| WARN hard error in the 2018 edition +} + +#[macro_export] +macro_rules! produces_async { + () => (pub fn r#async() {}) + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} + +#[macro_export] +macro_rules! consumes_async { + (r#async) => (1) + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} 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 0000000000000..cc400c2a92e0e --- /dev/null +++ b/src/test/ui/rust-2018/async-ident.rs @@ -0,0 +1,88 @@ +// 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)] +#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)] +#![deny(async_idents)] + +// edition:2015 +// run-rustfix + +fn async() {} //~ ERROR async +//~^ WARN hard error in the 2018 edition + +macro_rules! foo { + ($foo:ident) => {}; + ($async:expr, async) => {}; + //~^ ERROR async + //~| ERROR async + //~| WARN hard error in the 2018 edition + //~| WARN hard error in the 2018 edition +} + +foo!(async); + +mod dont_lint_raw { + fn r#async() {} +} + +mod async_trait { + trait async {} + //~^ ERROR async + //~| WARN hard error in the 2018 edition + struct MyStruct; + impl async for MyStruct {} + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} + +mod async_static { + static async: u32 = 0; + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} + +mod async_const { + const async: u32 = 0; + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} + +struct Foo; +impl Foo { fn async() {} } + //~^ ERROR async + //~| WARN hard error in the 2018 edition + +fn main() { + struct async {} + //~^ ERROR async + //~| WARN hard error in the 2018 edition + let async: async = async {}; + //~^ ERROR async + //~| WARN hard error in the 2018 edition + //~| ERROR async + //~| WARN hard error in the 2018 edition + //~| ERROR async + //~| WARN hard error in the 2018 edition +} + +#[macro_export] +macro_rules! produces_async { + () => (pub fn async() {}) + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} + +#[macro_export] +macro_rules! consumes_async { + (async) => (1) + //~^ ERROR async + //~| WARN hard error in the 2018 edition +} 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 0000000000000..94fd3e70434cb --- /dev/null +++ b/src/test/ui/rust-2018/async-ident.stderr @@ -0,0 +1,133 @@ +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:18: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:13:9 + | +LL | #![deny(async_idents)] + | ^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:23:7 + | +LL | ($async:expr, async) => {}; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:23:19 + | +LL | ($async:expr, async) => {}; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:37:11 + | +LL | trait async {} + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:41:10 + | +LL | impl async for MyStruct {} + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:47:12 + | +LL | static async: u32 = 0; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:53:11 + | +LL | const async: u32 = 0; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:59:15 + | +LL | impl Foo { fn async() {} } + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:64:12 + | +LL | struct async {} + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:67:9 + | +LL | let async: async = async {}; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:67:16 + | +LL | let async: async = async {}; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:67:24 + | +LL | let async: async = async {}; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:78:19 + | +LL | () => (pub fn async() {}) + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `async` is a keyword in the 2018 edition + --> $DIR/async-ident.rs:85:6 + | +LL | (async) => (1) + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#async` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: aborting due to 14 previous errors +