diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 4babadb67bce6..9c6d03af9f51b 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -469,6 +469,7 @@ rustc_dep_node_append!([define_dep_nodes!][ <'tcx> [] UnsafetyCheckResult(DefId), [] UnsafeDeriveOnReprPacked(DefId), + [] LintMod(DefId), [] CheckModAttrs(DefId), [] CheckModLoops(DefId), [] CheckModUnstableApiUsage(DefId), diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index d810a9310c55f..2ffb4959951b8 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -580,17 +580,17 @@ impl<'hir> Map<'hir> { &self.forest.krate.attrs } - pub fn get_module(&self, module: DefId) -> (&'hir Mod, Span, NodeId) - { + pub fn get_module(&self, module: DefId) -> (&'hir Mod, Span, HirId) { let node_id = self.as_local_node_id(module).unwrap(); + let hir_id = self.node_to_hir_id(node_id); self.read(node_id); match self.find_entry(node_id).unwrap().node { Node::Item(&Item { span, node: ItemKind::Mod(ref m), .. - }) => (m, span, node_id), - Node::Crate => (&self.forest.krate.module, self.forest.krate.span, node_id), + }) => (m, span, hir_id), + Node::Crate => (&self.forest.krate.module, self.forest.krate.span, hir_id), _ => panic!("not a module") } } @@ -1013,7 +1013,7 @@ impl<'hir> Map<'hir> { /// corresponding to the Node ID pub fn attrs(&self, id: NodeId) -> &'hir [ast::Attribute] { self.read(id); // reveals attributes on the node - let attrs = match self.find(id) { + let attrs = match self.find_entry(id).map(|entry| entry.node) { Some(Node::Local(l)) => Some(&l.attrs[..]), Some(Node::Item(i)) => Some(&i.attrs[..]), Some(Node::ForeignItem(fi)) => Some(&fi.attrs[..]), @@ -1027,6 +1027,7 @@ impl<'hir> Map<'hir> { // Unit/tuple structs/variants take the attributes straight from // the struct/variant definition. Some(Node::Ctor(..)) => return self.attrs(self.get_parent(id)), + Some(Node::Crate) => Some(&self.forest.krate.attrs[..]), _ => None }; attrs.unwrap_or(&[]) diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 94f1d9c271007..953d0116aa2ba 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -27,6 +27,7 @@ use crate::rustc_serialize::{Decoder, Decodable, Encoder, Encodable}; use crate::session::{config, early_error, Session}; use crate::ty::{self, TyCtxt, Ty}; use crate::ty::layout::{LayoutError, LayoutOf, TyLayout}; +use crate::ty::query::Providers; use crate::util::nodemap::FxHashMap; use crate::util::common::time; @@ -36,8 +37,9 @@ use syntax::edition; use syntax_pos::{MultiSpan, Span, symbol::{LocalInternedString, Symbol}}; use errors::DiagnosticBuilder; use crate::hir; -use crate::hir::def_id::LOCAL_CRATE; +use crate::hir::def_id::{DefId, LOCAL_CRATE}; use crate::hir::intravisit as hir_visit; +use crate::hir::intravisit::Visitor; use syntax::util::lev_distance::find_best_match_for_name; use syntax::visit as ast_visit; @@ -55,6 +57,7 @@ pub struct LintStore { pre_expansion_passes: Option>, early_passes: Option>, late_passes: Option>, + late_module_passes: Option>, /// Lints indexed by name. by_name: FxHashMap, @@ -150,6 +153,7 @@ impl LintStore { pre_expansion_passes: Some(vec![]), early_passes: Some(vec![]), late_passes: Some(vec![]), + late_module_passes: Some(vec![]), by_name: Default::default(), future_incompatible: Default::default(), lint_groups: Default::default(), @@ -199,9 +203,14 @@ impl LintStore { pub fn register_late_pass(&mut self, sess: Option<&Session>, from_plugin: bool, + per_module: bool, pass: LateLintPassObject) { self.push_pass(sess, from_plugin, &pass); - self.late_passes.as_mut().unwrap().push(pass); + if per_module { + self.late_module_passes.as_mut().unwrap().push(pass); + } else { + self.late_passes.as_mut().unwrap().push(pass); + } } // Helper method for register_early/late_pass @@ -508,6 +517,7 @@ pub struct LateContext<'a, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'tcx, 'tcx>, /// Side-tables for the body we are in. + // FIXME: Make this lazy to avoid running the TypeckTables query? pub tables: &'a ty::TypeckTables<'tcx>, /// Parameter environment for the item we are in. @@ -523,6 +533,9 @@ pub struct LateContext<'a, 'tcx: 'a> { /// Generic type parameters in scope for the item we are in. pub generics: Option<&'tcx hir::Generics>, + + /// We are only looking at one module + only_module: bool, } /// Context for lint checking of the AST, after expansion, before lowering to @@ -803,6 +816,12 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> { pub fn current_lint_root(&self) -> hir::HirId { self.last_node_with_lint_attrs } + + fn process_mod(&mut self, m: &'tcx hir::Mod, s: Span, n: hir::HirId) { + run_lints!(self, check_mod, m, s, n); + hir_visit::walk_mod(self, m, n); + run_lints!(self, check_mod_post, m, s, n); + } } impl<'a, 'tcx> LayoutOf for LateContext<'a, 'tcx> { @@ -934,9 +953,9 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { } fn visit_mod(&mut self, m: &'tcx hir::Mod, s: Span, n: hir::HirId) { - run_lints!(self, check_mod, m, s, n); - hir_visit::walk_mod(self, m, n); - run_lints!(self, check_mod_post, m, s, n); + if !self.only_module { + self.process_mod(m, s, n); + } } fn visit_local(&mut self, l: &'tcx hir::Local) { @@ -1203,11 +1222,48 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } } +pub fn lint_mod<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) { + let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); -/// Performs lint checking on a crate. -/// -/// Consumes the `lint_store` field of the `Session`. -pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + let store = &tcx.sess.lint_store; + let passes = store.borrow_mut().late_module_passes.take(); + + let mut cx = LateContext { + tcx, + tables: &ty::TypeckTables::empty(None), + param_env: ty::ParamEnv::empty(), + access_levels, + lint_sess: LintSession { + lints: store.borrow(), + passes, + }, + last_node_with_lint_attrs: tcx.hir().as_local_hir_id(module_def_id).unwrap(), + generics: None, + only_module: true, + }; + + let (module, span, hir_id) = tcx.hir().get_module(module_def_id); + cx.process_mod(module, span, hir_id); + + // Visit the crate attributes + if hir_id == hir::CRATE_HIR_ID { + walk_list!(cx, visit_attribute, cx.tcx.hir().attrs_by_hir_id(hir::CRATE_HIR_ID)); + } + + // Put the lint store levels and passes back in the session. + let passes = cx.lint_sess.passes; + drop(cx.lint_sess.lints); + store.borrow_mut().late_module_passes = passes; +} + +pub(crate) fn provide(providers: &mut Providers<'_>) { + *providers = Providers { + lint_mod, + ..*providers + }; +} + +fn lint_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); let krate = tcx.hir().krate(); @@ -1225,6 +1281,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { }, last_node_with_lint_attrs: hir::CRATE_HIR_ID, generics: None, + only_module: false, }; // Visit the whole crate. @@ -1244,6 +1301,17 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.sess.lint_store.borrow_mut().late_passes = passes; } +/// Performs lint checking on a crate. +pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + // Run per-module lints + for &module in tcx.hir().krate().modules.keys() { + tcx.ensure().lint_mod(tcx.hir().local_def_id(module)); + } + + // Run whole crate non-incremental lints + lint_crate(tcx); +} + struct EarlyLintPassObjects<'a> { lints: &'a mut [EarlyLintPassObject], } diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index ae44210bcfffc..cf1c5d50000fa 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -824,6 +824,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'a, 'tcx> { pub fn provide(providers: &mut Providers<'_>) { providers.lint_levels = lint_levels; + context::provide(providers); } /// Returns whether `span` originates in a foreign crate's external macro. diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index 85c2afd1159a1..9c705104d1888 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -217,6 +217,8 @@ rustc_query_append! { [define_queries!][ <'tcx> }, Other { + [] fn lint_mod: LintMod(DefId) -> (), + /// Checks the attributes in the module [] fn check_mod_attrs: CheckModAttrs(DefId) -> (), diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index adac19d3410b2..b2b141fd0f514 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -1262,6 +1262,7 @@ pub fn force_from_dep_node<'tcx>( DepKind::MirBorrowCheck => { force!(mir_borrowck, def_id!()); } DepKind::UnsafetyCheckResult => { force!(unsafety_check_result, def_id!()); } DepKind::UnsafeDeriveOnReprPacked => { force!(unsafe_derive_on_repr_packed, def_id!()); } + DepKind::LintMod => { force!(lint_mod, def_id!()); } DepKind::CheckModAttrs => { force!(check_mod_attrs, def_id!()); } DepKind::CheckModLoops => { force!(check_mod_loops, def_id!()); } DepKind::CheckModUnstableApiUsage => { force!(check_mod_unstable_api_usage, def_id!()); } diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index c199829b298c0..b6daadb1b456f 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -328,7 +328,7 @@ pub fn register_plugins<'a>( ls.register_early_pass(Some(sess), true, false, pass); } for pass in late_lint_passes { - ls.register_late_pass(Some(sess), true, pass); + ls.register_late_pass(Some(sess), true, false, pass); } for (name, (to, deprecated_name)) in lint_groups { diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 304e6eb712e3c..492ac1bf14dcc 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1360,6 +1360,7 @@ fn check_const(cx: &LateContext<'_, '_>, body_id: hir::BodyId) { promoted: None }; // trigger the query once for all constants since that will already report the errors + // FIXME: Use ensure here let _ = cx.tcx.const_eval(param_env.and(cid)); } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 73a32f6d2003a..4c624a267af9b 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -125,37 +125,72 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { store.register_early_pass(sess, false, true, box BuiltinCombinedEarlyLintPass::new()); } - late_lint_methods!(declare_combined_late_lint_pass, [BuiltinCombinedLateLintPass, [ + late_lint_methods!(declare_combined_late_lint_pass, [BuiltinCombinedModuleLateLintPass, [ HardwiredLints: HardwiredLints, WhileTrue: WhileTrue, ImproperCTypes: ImproperCTypes, VariantSizeDifferences: VariantSizeDifferences, BoxPointers: BoxPointers, - UnusedAttributes: UnusedAttributes, PathStatements: PathStatements, + + // Depends on referenced function signatures in expressions UnusedResults: UnusedResults, - NonSnakeCase: NonSnakeCase, + NonUpperCaseGlobals: NonUpperCaseGlobals, NonShorthandFieldPatterns: NonShorthandFieldPatterns, UnusedAllocation: UnusedAllocation, + + // Depends on types used in type definitions MissingCopyImplementations: MissingCopyImplementations, - UnstableFeatures: UnstableFeatures, - InvalidNoMangleItems: InvalidNoMangleItems, + PluginAsLibrary: PluginAsLibrary, + + // Depends on referenced function signatures in expressions MutableTransmutes: MutableTransmutes, + + // Depends on types of fields, checks if they implement Drop UnionsWithDropFields: UnionsWithDropFields, - UnreachablePub: UnreachablePub, - UnnameableTestItems: UnnameableTestItems::new(), + TypeAliasBounds: TypeAliasBounds, - UnusedBrokenConst: UnusedBrokenConst, + TrivialConstraints: TrivialConstraints, TypeLimits: TypeLimits::new(), + + NonSnakeCase: NonSnakeCase, + InvalidNoMangleItems: InvalidNoMangleItems, + + // Depends on access levels + UnreachablePub: UnreachablePub, + + ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, + ]], ['tcx]); + + store.register_late_pass(sess, false, true, box BuiltinCombinedModuleLateLintPass::new()); + + late_lint_methods!(declare_combined_late_lint_pass, [BuiltinCombinedLateLintPass, [ + // FIXME: Look into regression when this is used as a module lint + // May Depend on constants elsewhere + UnusedBrokenConst: UnusedBrokenConst, + + // Uses attr::is_used which is untracked, can't be an incremental module pass. + UnusedAttributes: UnusedAttributes, + + // Needs to run after UnusedAttributes as it marks all `feature` attributes as used. + UnstableFeatures: UnstableFeatures, + + // Tracks state across modules + UnnameableTestItems: UnnameableTestItems::new(), + + // Tracks attributes of parents MissingDoc: MissingDoc::new(), + + // Depends on access levels + // FIXME: Turn the computation of types which implement Debug into a query + // and change this to a module lint pass MissingDebugImplementations: MissingDebugImplementations::new(), - ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, ]], ['tcx]); - store.register_late_pass(sess, false, box BuiltinCombinedLateLintPass::new()); + store.register_late_pass(sess, false, false, box BuiltinCombinedLateLintPass::new()); add_lint_group!(sess, "nonstandard_style", diff --git a/src/librustc_lint/nonstandard_style.rs b/src/librustc_lint/nonstandard_style.rs index fa18dd1eb8ddb..7a164dbcdf12d 100644 --- a/src/librustc_lint/nonstandard_style.rs +++ b/src/librustc_lint/nonstandard_style.rs @@ -267,11 +267,15 @@ impl LintPass for NonSnakeCase { } impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonSnakeCase { - fn check_crate(&mut self, cx: &LateContext<'_, '_>, cr: &hir::Crate) { + fn check_mod(&mut self, cx: &LateContext<'_, '_>, _: &'tcx hir::Mod, _: Span, id: hir::HirId) { + if id != hir::CRATE_HIR_ID { + return; + } + let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name { Some(Ident::from_str(name)) } else { - attr::find_by_name(&cr.attrs, "crate_name") + attr::find_by_name(&cx.tcx.hir().attrs_by_hir_id(hir::CRATE_HIR_ID), "crate_name") .and_then(|attr| attr.meta()) .and_then(|meta| { meta.name_value_literal().and_then(|lit| { diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index bbd03e82a3730..b532e5c94d531 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -1790,8 +1790,7 @@ fn check_mod_privacy<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) { current_item: hir::DUMMY_HIR_ID, empty_tables: &empty_tables, }; - let (module, span, node_id) = tcx.hir().get_module(module_def_id); - let hir_id = tcx.hir().node_to_hir_id(node_id); + let (module, span, hir_id) = tcx.hir().get_module(module_def_id); intravisit::walk_mod(&mut visitor, module, hir_id); // Check privacy of explicitly written types and traits as well as diff --git a/src/test/ui/lint/lint-impl-fn.stderr b/src/test/ui/lint/lint-impl-fn.stderr index 2c9a264287c96..56f85111d428f 100644 --- a/src/test/ui/lint/lint-impl-fn.stderr +++ b/src/test/ui/lint/lint-impl-fn.stderr @@ -11,25 +11,25 @@ LL | #[deny(while_true)] | ^^^^^^^^^^ error: denote infinite loops with `loop { ... }` - --> $DIR/lint-impl-fn.rs:18:25 + --> $DIR/lint-impl-fn.rs:27:5 | -LL | fn foo(&self) { while true {} } - | ^^^^^^^^^^ help: use `loop` +LL | while true {} + | ^^^^^^^^^^ help: use `loop` | note: lint level defined here - --> $DIR/lint-impl-fn.rs:13:8 + --> $DIR/lint-impl-fn.rs:25:8 | LL | #[deny(while_true)] | ^^^^^^^^^^ error: denote infinite loops with `loop { ... }` - --> $DIR/lint-impl-fn.rs:27:5 + --> $DIR/lint-impl-fn.rs:18:25 | -LL | while true {} - | ^^^^^^^^^^ help: use `loop` +LL | fn foo(&self) { while true {} } + | ^^^^^^^^^^ help: use `loop` | note: lint level defined here - --> $DIR/lint-impl-fn.rs:25:8 + --> $DIR/lint-impl-fn.rs:13:8 | LL | #[deny(while_true)] | ^^^^^^^^^^ diff --git a/src/test/ui/lint/suggestions.stderr b/src/test/ui/lint/suggestions.stderr index c28814aeee8f9..5aaa9947f998a 100644 --- a/src/test/ui/lint/suggestions.stderr +++ b/src/test/ui/lint/suggestions.stderr @@ -65,6 +65,24 @@ LL | pub fn defiant(_t: T) {} | = note: #[warn(no_mangle_generic_items)] on by default +warning: denote infinite loops with `loop { ... }` + --> $DIR/suggestions.rs:46:5 + | +LL | while true { + | ^^^^^^^^^^ help: use `loop` + | + = note: #[warn(while_true)] on by default + +warning: the `warp_factor:` in this pattern is redundant + --> $DIR/suggestions.rs:61:23 + | +LL | Equinox { warp_factor: warp_factor } => {} + | ------------^^^^^^^^^^^^ + | | + | help: remove this + | + = note: #[warn(non_shorthand_field_patterns)] on by default + error: const items should never be #[no_mangle] --> $DIR/suggestions.rs:22:18 | @@ -97,23 +115,5 @@ LL | #[no_mangle] pub(crate) fn crossfield() {} | | | help: remove this attribute -warning: denote infinite loops with `loop { ... }` - --> $DIR/suggestions.rs:46:5 - | -LL | while true { - | ^^^^^^^^^^ help: use `loop` - | - = note: #[warn(while_true)] on by default - -warning: the `warp_factor:` in this pattern is redundant - --> $DIR/suggestions.rs:61:23 - | -LL | Equinox { warp_factor: warp_factor } => {} - | ------------^^^^^^^^^^^^ - | | - | help: remove this - | - = note: #[warn(non_shorthand_field_patterns)] on by default - error: aborting due to 3 previous errors