1
Fork 0

Do not visit whole crate to compute lints_that_dont_need_to_run.

This commit is contained in:
Camille GILLOT 2024-12-03 00:12:24 +00:00
parent f174fd716a
commit 280a1d8edb
10 changed files with 79 additions and 95 deletions

View file

@ -1,10 +1,11 @@
use rustc_ast::attr::AttributeExt; use rustc_ast::attr::AttributeExt;
use rustc_ast_pretty::pprust; 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_errors::{Diag, LintDiagnostic, MultiSpan};
use rustc_feature::{Features, GateIssue}; use rustc_feature::{Features, GateIssue};
use rustc_hir::HirId;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{CRATE_HIR_ID, HirId};
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
@ -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 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 mut dont_need_to_run: FxHashSet<LintId> = store
let dont_need_to_run: FxIndexSet<LintId> = store
.get_lints() .get_lints()
.into_iter() .into_iter()
.filter(|lint| { .filter(|lint| {
@ -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()); lint.future_incompatible.is_some_and(|fut| fut.reason.has_future_breakage());
!has_future_breakage && !lint.eval_always !has_future_breakage && !lint.eval_always
}) })
.filter_map(|lint| { .filter(|lint| {
let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID); let lint_level =
if matches!(lint_level.level, Level::Allow) root_map.lint_level_id_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
|| (matches!(lint_level.src, LintLevelSource::Default)) // Only include lints that are allowed at crate root or by default.
&& lint.default_level(tcx.sess.edition()) == Level::Allow matches!(lint_level.level, Level::Allow)
{ || (matches!(lint_level.src, LintLevelSource::Default)
Some(LintId::of(lint)) && lint.default_level(tcx.sess.edition()) == Level::Allow)
} else {
None
}
}) })
.map(|lint| LintId::of(*lint))
.collect(); .collect();
let mut visitor = LintLevelMaximum { tcx, dont_need_to_run }; for owner in tcx.hir_crate_items(()).owners() {
visitor.process_opts(); let map = tcx.shallow_lint_levels_on(owner);
tcx.hir_walk_attributes(&mut visitor);
visitor.dont_need_to_run // 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);
}
}
}
}
dont_need_to_run.into()
} }
#[instrument(level = "trace", skip(tcx), ret)] #[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<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> { pub struct LintLevelsBuilder<'s, P> {
sess: &'s Session, sess: &'s Session,
features: &'s Features, features: &'s Features,

View file

@ -476,7 +476,7 @@ rustc_queries! {
desc { "computing `#[expect]`ed lints in this crate" } 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 arena_cache
desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" } desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" }
} }

View file

@ -30,6 +30,8 @@ pub fn e() {}
//~| ERROR: expected identifier, found keyword `unsafe` //~| 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
//~| ERROR: malformed lint attribute input
//~| ERROR: malformed lint attribute input
pub fn f() {} pub fn f() {}
fn main() {} fn main() {}

View file

@ -114,6 +114,22 @@ LL | #[unsafe(allow(unsafe(dead_code)))]
| |
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` = 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`. For more information about this error, try `rustc --explain E0452`.

View file

@ -26,6 +26,14 @@ LL | #[deny("literal")]
| |
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` = 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`. For more information about this error, try `rustc --explain E0452`.

View file

@ -7,4 +7,5 @@ struct S;
#[deny("literal")] //~ ERROR malformed lint attribute input #[deny("literal")] //~ ERROR malformed lint attribute input
//[duplicate]~| ERROR malformed lint attribute input //[duplicate]~| ERROR malformed lint attribute input
//[duplicate]~| ERROR malformed lint attribute input
fn main() {} fn main() {}

View file

@ -1,4 +1,5 @@
#[warn(foo::bar)] #[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` //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar`
//~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar`
fn main() {} fn main() {}

View file

@ -15,6 +15,15 @@ LL | #[warn(foo::bar)]
= help: add `#![register_tool(foo)]` to the crate root = help: add `#![register_tool(foo)]` to the crate root
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` = 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`. For more information about this error, try `rustc --explain E0710`.

View file

@ -4,4 +4,5 @@
#[allow(foo::bar)] //~ ERROR unknown tool name `foo` found in scoped lint: `foo::bar` #[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`
//~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar`
fn main() {} fn main() {}

View file

@ -41,6 +41,15 @@ LL | #![deny(foo::bar)]
= help: add `#![register_tool(foo)]` to the crate root = help: add `#![register_tool(foo)]` to the crate root
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` = 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`. For more information about this error, try `rustc --explain E0710`.