Skip to content

Commit 0776294

Browse files
committed
perf: skip disallowed-profile resolution when the crate has no profile attributes
The disallowed_methods and disallowed_types lints resolved active profiles for every path, method call and type node, walking HIR parents and caching one entry per visited HirId even though almost no crate uses clippy::disallowed_profile attributes. Scan the crate's attribute maps once on first use and skip resolution entirely when no such attribute exists.
1 parent 824aa1f commit 0776294

1 file changed

Lines changed: 35 additions & 8 deletions

File tree

clippy_utils/src/disallowed_profiles.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,17 @@ impl ProfileSelection {
4646
#[derive(Default)]
4747
pub struct ProfileResolver {
4848
cache: FxHashMap<HirId, Option<ProfileSelection>>,
49+
/// Whether the crate has any `#[clippy::disallowed_profile(s)]` attribute, computed lazily on first use.
50+
has_profile_attrs: Option<bool>,
4951
}
5052

5153
impl ProfileResolver {
5254
pub fn active_profiles(&mut self, cx: &LateContext<'_>, hir_id: HirId) -> Option<&ProfileSelection> {
55+
// Common case: no profile attributes anywhere, so skip the HIR parent walk and the cache entirely.
56+
if !self.crate_has_profile_attrs(cx) {
57+
return None;
58+
}
59+
5360
// NOTE: The `contains_key`+`get` dance is intentional: using only `get` here triggers borrowck
5461
// errors because we need to mutate `self.cache` on cache misses.
5562
if self.cache.contains_key(&hir_id) {
@@ -66,6 +73,18 @@ impl ProfileResolver {
6673
self.cache.get(&hir_id).and_then(|selection| selection.as_ref())
6774
}
6875

76+
fn crate_has_profile_attrs(&mut self, cx: &LateContext<'_>) -> bool {
77+
*self.has_profile_attrs.get_or_insert_with(|| {
78+
cx.tcx.hir_crate_items(()).owners().any(|owner| {
79+
cx.tcx
80+
.hir_attr_map(owner)
81+
.map
82+
.values()
83+
.any(|attrs| attrs.iter().any(|attr| profile_attr_name(attr).is_some()))
84+
})
85+
})
86+
}
87+
6988
fn resolve(&self, cx: &LateContext<'_>, start: HirId) -> (Option<ProfileSelection>, SmallVec<[HirId; 8]>) {
7089
let mut visited = SmallVec::<[HirId; 8]>::new();
7190
let mut current = Some(start);
@@ -92,19 +111,27 @@ impl ProfileResolver {
92111
}
93112
}
94113

114+
/// Returns `disallowed_profile` or `disallowed_profiles` if `attr` is the corresponding
115+
/// `clippy::` attribute, and `None` for every other attribute.
116+
fn profile_attr_name(attr: &Attribute) -> Option<Symbol> {
117+
let path = attr.path();
118+
if path.len() == 2
119+
&& path[0] == sym::clippy
120+
&& (path[1] == sym::disallowed_profile || path[1] == sym::disallowed_profiles)
121+
{
122+
Some(path[1])
123+
} else {
124+
None
125+
}
126+
}
127+
95128
fn profiles_from_attrs(cx: &LateContext<'_>, attrs: &[Attribute]) -> Option<ProfileSelection> {
96129
let mut entries = SmallVec::<[ProfileEntry; 2]>::new();
97130

98131
for attr in attrs {
99-
let path = attr.path();
100-
if path.len() != 2 || path[0] != sym::clippy {
132+
let Some(name) = profile_attr_name(attr) else {
101133
continue;
102-
}
103-
104-
let name = path[1];
105-
if name != sym::disallowed_profile && name != sym::disallowed_profiles {
106-
continue;
107-
}
134+
};
108135

109136
let attr_label = if name == sym::disallowed_profiles {
110137
"`clippy::disallowed_profiles`"

0 commit comments

Comments
 (0)