From 280a1d8edb8322280bb9b10fab41c7bfb43d67b2 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 3 Dec 2024 00:12:24 +0000 Subject: [PATCH] Do not visit whole crate to compute `lints_that_dont_need_to_run`. --- compiler/rustc_lint/src/levels.rs | 117 ++++-------------- compiler/rustc_middle/src/query/mod.rs | 2 +- .../unsafe/proc-unsafe-attributes.rs | 2 + .../unsafe/proc-unsafe-attributes.stderr | 18 ++- .../deduplicate-diagnostics.duplicate.stderr | 10 +- tests/ui/deduplicate-diagnostics.rs | 1 + tests/ui/tool-attributes/tool_lints.rs | 1 + tests/ui/tool-attributes/tool_lints.stderr | 11 +- .../tool-attributes/unknown-lint-tool-name.rs | 1 + .../unknown-lint-tool-name.stderr | 11 +- 10 files changed, 79 insertions(+), 95 deletions(-) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index f1fe07cfcfaa4..d0b1e7bf25534 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,10 +1,11 @@ use rustc_ast::attr::AttributeExt; use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::unord::UnordSet; use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; use rustc_feature::{Features, GateIssue}; +use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{CRATE_HIR_ID, HirId}; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::hir::nested_filter; @@ -115,12 +116,11 @@ impl LintLevelSets { } } -fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { +fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet { let store = unerased_lint_store(&tcx.sess); + let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID); - let map = tcx.shallow_lint_levels_on(rustc_hir::CRATE_OWNER_ID); - - let dont_need_to_run: FxIndexSet = store + let mut dont_need_to_run: FxHashSet = store .get_lints() .into_iter() .filter(|lint| { @@ -129,24 +129,31 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { lint.future_incompatible.is_some_and(|fut| fut.reason.has_future_breakage()); !has_future_breakage && !lint.eval_always }) - .filter_map(|lint| { - let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID); - if matches!(lint_level.level, Level::Allow) - || (matches!(lint_level.src, LintLevelSource::Default)) - && lint.default_level(tcx.sess.edition()) == Level::Allow - { - Some(LintId::of(lint)) - } else { - None - } + .filter(|lint| { + let lint_level = + root_map.lint_level_id_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID); + // Only include lints that are allowed at crate root or by default. + matches!(lint_level.level, Level::Allow) + || (matches!(lint_level.src, LintLevelSource::Default) + && lint.default_level(tcx.sess.edition()) == Level::Allow) }) + .map(|lint| LintId::of(*lint)) .collect(); - let mut visitor = LintLevelMaximum { tcx, dont_need_to_run }; - visitor.process_opts(); - tcx.hir_walk_attributes(&mut visitor); + for owner in tcx.hir_crate_items(()).owners() { + let map = tcx.shallow_lint_levels_on(owner); + + // All lints that appear with a non-allow level must be run. + for (_, specs) in map.specs.iter() { + for (lint, level_and_source) in specs.iter() { + if !matches!(level_and_source.level, Level::Allow) { + dont_need_to_run.remove(lint); + } + } + } + } - visitor.dont_need_to_run + dont_need_to_run.into() } #[instrument(level = "trace", skip(tcx), ret)] @@ -340,76 +347,6 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { } } -/// Visitor with the only function of visiting every item-like in a crate and -/// computing the highest level that every lint gets put to. -/// -/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item -/// uses #[warn(lint)], this visitor will set that lint level as `Warn` -struct LintLevelMaximum<'tcx> { - tcx: TyCtxt<'tcx>, - /// The actual list of detected lints. - dont_need_to_run: FxIndexSet, -} - -impl<'tcx> LintLevelMaximum<'tcx> { - fn process_opts(&mut self) { - let store = unerased_lint_store(self.tcx.sess); - for (lint_group, level) in &self.tcx.sess.opts.lint_opts { - if *level != Level::Allow { - let Ok(lints) = store.find_lints(lint_group) else { - return; - }; - for lint in lints { - self.dont_need_to_run.swap_remove(&lint); - } - } - } - } -} - -impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { - type NestedFilter = nested_filter::All; - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tcx - } - - /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s, - /// but that is handled with more care - fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) { - if matches!( - Level::from_attr(attribute), - Some((Level::Warn | Level::Deny | Level::Forbid | Level::Expect | Level::ForceWarn, _)) - ) { - let store = unerased_lint_store(self.tcx.sess); - // Lint attributes are always a metalist inside a - // metalist (even with just one lint). - let Some(meta_item_list) = attribute.meta_item_list() else { return }; - - for meta_list in meta_item_list { - // Convert Path to String - let Some(meta_item) = meta_list.meta_item() else { return }; - let ident: &str = &meta_item - .path - .segments - .iter() - .map(|segment| segment.ident.as_str()) - .collect::>() - .join("::"); - let Ok(lints) = store.find_lints( - // Lint attributes can only have literals - ident, - ) else { - return; - }; - for lint in lints { - self.dont_need_to_run.swap_remove(&lint); - } - } - } - } -} - pub struct LintLevelsBuilder<'s, P> { sess: &'s Session, features: &'s Features, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 698859c663b17..b9a113b976e46 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -476,7 +476,7 @@ rustc_queries! { desc { "computing `#[expect]`ed lints in this crate" } } - query lints_that_dont_need_to_run(_: ()) -> &'tcx FxIndexSet { + query lints_that_dont_need_to_run(_: ()) -> &'tcx UnordSet { arena_cache desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" } } diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs index eaf8706369a5a..2f17d9620b411 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs @@ -30,6 +30,8 @@ pub fn e() {} //~| ERROR: expected identifier, found keyword `unsafe` //~| ERROR: malformed lint attribute input //~| ERROR: malformed lint attribute input +//~| ERROR: malformed lint attribute input +//~| ERROR: malformed lint attribute input pub fn f() {} fn main() {} diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr index 9c5751c82e4c4..25b83a26e1700 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr @@ -114,6 +114,22 @@ LL | #[unsafe(allow(unsafe(dead_code)))] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 15 previous errors +error[E0452]: malformed lint attribute input + --> $DIR/proc-unsafe-attributes.rs:26:16 + | +LL | #[unsafe(allow(unsafe(dead_code)))] + | ^^^^^^^^^^^^^^^^^ bad attribute argument + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0452]: malformed lint attribute input + --> $DIR/proc-unsafe-attributes.rs:26:16 + | +LL | #[unsafe(allow(unsafe(dead_code)))] + | ^^^^^^^^^^^^^^^^^ bad attribute argument + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 17 previous errors For more information about this error, try `rustc --explain E0452`. diff --git a/tests/ui/deduplicate-diagnostics.duplicate.stderr b/tests/ui/deduplicate-diagnostics.duplicate.stderr index 0544b993278d7..48e2ba7b86aa4 100644 --- a/tests/ui/deduplicate-diagnostics.duplicate.stderr +++ b/tests/ui/deduplicate-diagnostics.duplicate.stderr @@ -26,6 +26,14 @@ LL | #[deny("literal")] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 4 previous errors +error[E0452]: malformed lint attribute input + --> $DIR/deduplicate-diagnostics.rs:8:8 + | +LL | #[deny("literal")] + | ^^^^^^^^^ bad attribute argument + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0452`. diff --git a/tests/ui/deduplicate-diagnostics.rs b/tests/ui/deduplicate-diagnostics.rs index 54bd5dd5098c1..299c1f5f46108 100644 --- a/tests/ui/deduplicate-diagnostics.rs +++ b/tests/ui/deduplicate-diagnostics.rs @@ -7,4 +7,5 @@ struct S; #[deny("literal")] //~ ERROR malformed lint attribute input //[duplicate]~| ERROR malformed lint attribute input + //[duplicate]~| ERROR malformed lint attribute input fn main() {} diff --git a/tests/ui/tool-attributes/tool_lints.rs b/tests/ui/tool-attributes/tool_lints.rs index ef27532f6de1f..9e4aa7a939acb 100644 --- a/tests/ui/tool-attributes/tool_lints.rs +++ b/tests/ui/tool-attributes/tool_lints.rs @@ -1,4 +1,5 @@ #[warn(foo::bar)] //~^ ERROR unknown tool name `foo` found in scoped lint: `foo::bar` //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` +//~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` fn main() {} diff --git a/tests/ui/tool-attributes/tool_lints.stderr b/tests/ui/tool-attributes/tool_lints.stderr index f1d825caba159..eee0a9784ece8 100644 --- a/tests/ui/tool-attributes/tool_lints.stderr +++ b/tests/ui/tool-attributes/tool_lints.stderr @@ -15,6 +15,15 @@ LL | #[warn(foo::bar)] = help: add `#![register_tool(foo)]` to the crate root = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` + --> $DIR/tool_lints.rs:1:8 + | +LL | #[warn(foo::bar)] + | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0710`. diff --git a/tests/ui/tool-attributes/unknown-lint-tool-name.rs b/tests/ui/tool-attributes/unknown-lint-tool-name.rs index 59fc56d820ee2..84ab7c1944ab2 100644 --- a/tests/ui/tool-attributes/unknown-lint-tool-name.rs +++ b/tests/ui/tool-attributes/unknown-lint-tool-name.rs @@ -4,4 +4,5 @@ #[allow(foo::bar)] //~ ERROR unknown tool name `foo` found in scoped lint: `foo::bar` //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` + //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` fn main() {} diff --git a/tests/ui/tool-attributes/unknown-lint-tool-name.stderr b/tests/ui/tool-attributes/unknown-lint-tool-name.stderr index 5d99777a14ae3..91baf88273756 100644 --- a/tests/ui/tool-attributes/unknown-lint-tool-name.stderr +++ b/tests/ui/tool-attributes/unknown-lint-tool-name.stderr @@ -41,6 +41,15 @@ LL | #![deny(foo::bar)] = help: add `#![register_tool(foo)]` to the crate root = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 5 previous errors +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` + --> $DIR/unknown-lint-tool-name.rs:5:9 + | +LL | #[allow(foo::bar)] + | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0710`.