Skip to content

Do not visit whole crate to compute lints_that_dont_need_to_run. #133781

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 27 additions & 90 deletions compiler/rustc_lint/src/levels.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -115,12 +116,11 @@ impl LintLevelSets {
}
}

fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
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<LintId> = store
let mut dont_need_to_run: FxHashSet<LintId> = store
.get_lints()
.into_iter()
.filter(|lint| {
Expand All @@ -129,24 +129,31 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
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)]
Expand Down Expand Up @@ -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<LintId>,
}

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::<Vec<&str>>()
.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,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ rustc_queries! {
desc { "computing `#[expect]`ed lints in this crate" }
}

query lints_that_dont_need_to_run(_: ()) -> &'tcx FxIndexSet<LintId> {
query lints_that_dont_need_to_run(_: ()) -> &'tcx UnordSet<LintId> {
arena_cache
desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" }
}
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
18 changes: 17 additions & 1 deletion tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
10 changes: 9 additions & 1 deletion tests/ui/deduplicate-diagnostics.duplicate.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
1 change: 1 addition & 0 deletions tests/ui/deduplicate-diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
1 change: 1 addition & 0 deletions tests/ui/tool-attributes/tool_lints.rs
Original file line number Diff line number Diff line change
@@ -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() {}
11 changes: 10 additions & 1 deletion tests/ui/tool-attributes/tool_lints.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
1 change: 1 addition & 0 deletions tests/ui/tool-attributes/unknown-lint-tool-name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
11 changes: 10 additions & 1 deletion tests/ui/tool-attributes/unknown-lint-tool-name.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Loading