1use rustc_ast::{self as ast, MetaItemInner};
23use rustc_data_structures::fx::FxHashSet;
24use rustc_data_structures::unord::UnordSet;
25use rustc_hir::def_id::LocalDefId;
26use rustc_hir::{
27 Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit,
28};
29use rustc_middle::dep_graph::{DepNode, DepNodeExt, dep_kind_from_label, label_strs};
30use rustc_middle::hir::nested_filter;
31use rustc_middle::ty::TyCtxt;
32use rustc_span::{Span, Symbol, sym};
33use thin_vec::ThinVec;
34use tracing::debug;
35
36use crate::errors;
37
38const LOADED_FROM_DISK: Symbol = sym::loaded_from_disk;
39const EXCEPT: Symbol = sym::except;
40const CFG: Symbol = sym::cfg;
41
42const BASE_CONST: &[&str] = &[label_strs::type_of];
46
47const BASE_FN: &[&str] = &[
49 label_strs::fn_sig,
51 label_strs::generics_of,
52 label_strs::predicates_of,
53 label_strs::type_of,
54 label_strs::typeck,
57];
58
59const BASE_HIR: &[&str] = &[
61 label_strs::opt_hir_owner_nodes,
63];
64
65const BASE_IMPL: &[&str] =
67 &[label_strs::associated_item_def_ids, label_strs::generics_of, label_strs::impl_trait_header];
68
69const BASE_MIR: &[&str] = &[label_strs::optimized_mir, label_strs::promoted_mir];
72
73const BASE_STRUCT: &[&str] =
78 &[label_strs::generics_of, label_strs::predicates_of, label_strs::type_of];
79
80const EXTRA_ASSOCIATED: &[&str] = &[label_strs::associated_item];
83
84const EXTRA_TRAIT: &[&str] = &[];
85
86const LABELS_CONST: &[&[&str]] = &[BASE_HIR, BASE_CONST];
89
90const LABELS_CONST_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED];
92
93const LABELS_CONST_IN_TRAIT: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED, EXTRA_TRAIT];
95
96const LABELS_FN: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN];
98
99const LABELS_FN_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED];
101
102const LABELS_FN_IN_TRAIT: &[&[&str]] =
104 &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED, EXTRA_TRAIT];
105
106const LABELS_HIR_ONLY: &[&[&str]] = &[BASE_HIR];
108
109const LABELS_TRAIT: &[&[&str]] = &[
111 BASE_HIR,
112 &[label_strs::associated_item_def_ids, label_strs::predicates_of, label_strs::generics_of],
113];
114
115const LABELS_IMPL: &[&[&str]] = &[BASE_HIR, BASE_IMPL];
117
118const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT];
120
121type Labels = UnordSet<String>;
129
130struct Assertion {
132 clean: Labels,
133 dirty: Labels,
134 loaded_from_disk: Labels,
135}
136
137pub(crate) fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) {
138 if !tcx.sess.opts.unstable_opts.query_dep_graph {
139 return;
140 }
141
142 if !tcx.features().rustc_attrs() {
144 return;
145 }
146
147 tcx.dep_graph.with_ignore(|| {
148 let mut dirty_clean_visitor = DirtyCleanVisitor { tcx, checked_attrs: Default::default() };
149
150 let crate_items = tcx.hir_crate_items(());
151
152 for id in crate_items.free_items() {
153 dirty_clean_visitor.check_item(id.owner_id.def_id);
154 }
155
156 for id in crate_items.trait_items() {
157 dirty_clean_visitor.check_item(id.owner_id.def_id);
158 }
159
160 for id in crate_items.impl_items() {
161 dirty_clean_visitor.check_item(id.owner_id.def_id);
162 }
163
164 for id in crate_items.foreign_items() {
165 dirty_clean_visitor.check_item(id.owner_id.def_id);
166 }
167
168 let mut all_attrs = FindAllAttrs { tcx, found_attrs: ::alloc::vec::Vec::new()vec![] };
169 tcx.hir_walk_attributes(&mut all_attrs);
170
171 all_attrs.report_unchecked_attrs(dirty_clean_visitor.checked_attrs);
175 })
176}
177
178struct DirtyCleanVisitor<'tcx> {
179 tcx: TyCtxt<'tcx>,
180 checked_attrs: FxHashSet<ast::AttrId>,
181}
182
183impl<'tcx> DirtyCleanVisitor<'tcx> {
184 fn assertion_maybe(&mut self, item_id: LocalDefId, attr: &Attribute) -> Option<Assertion> {
186 if !attr.has_name(sym::rustc_clean) {
::core::panicking::panic("assertion failed: attr.has_name(sym::rustc_clean)")
};assert!(attr.has_name(sym::rustc_clean));
187 if !check_config(self.tcx, attr) {
188 return None;
190 }
191 let assertion = self.assertion_auto(item_id, attr);
192 Some(assertion)
193 }
194
195 fn assertion_auto(&mut self, item_id: LocalDefId, attr: &Attribute) -> Assertion {
197 let (name, mut auto) = self.auto_labels(item_id, attr);
198 let except = self.except(attr);
199 let loaded_from_disk = self.loaded_from_disk(attr);
200 for e in except.items().into_sorted_stable_ord() {
201 if !auto.remove(e) {
202 self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span(), name, e });
203 }
204 }
205 Assertion { clean: auto, dirty: except, loaded_from_disk }
206 }
207
208 fn loaded_from_disk(&self, attr: &Attribute) -> Labels {
210 for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
211 if item.has_name(LOADED_FROM_DISK) {
212 let value = expect_associated_value(self.tcx, &item);
213 return self.resolve_labels(&item, value);
214 }
215 }
216 Labels::default()
218 }
219
220 fn except(&self, attr: &Attribute) -> Labels {
222 for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
223 if item.has_name(EXCEPT) {
224 let value = expect_associated_value(self.tcx, &item);
225 return self.resolve_labels(&item, value);
226 }
227 }
228 Labels::default()
230 }
231
232 fn auto_labels(&mut self, item_id: LocalDefId, attr: &Attribute) -> (&'static str, Labels) {
235 let node = self.tcx.hir_node_by_def_id(item_id);
236 let (name, labels) = match node {
237 HirNode::Item(item) => {
238 match item.kind {
239 HirItem::Static(..) => ("ItemStatic", LABELS_CONST),
250
251 HirItem::Const(..) => ("ItemConst", LABELS_CONST),
253
254 HirItem::Fn { .. } => ("ItemFn", LABELS_FN),
256
257 HirItem::Mod(..) => ("ItemMod", LABELS_HIR_ONLY),
259
260 HirItem::ForeignMod { .. } => ("ItemForeignMod", LABELS_HIR_ONLY),
262
263 HirItem::GlobalAsm { .. } => ("ItemGlobalAsm", LABELS_HIR_ONLY),
265
266 HirItem::TyAlias(..) => ("ItemTy", LABELS_HIR_ONLY),
268
269 HirItem::Enum(..) => ("ItemEnum", LABELS_ADT),
271
272 HirItem::Struct(..) => ("ItemStruct", LABELS_ADT),
274
275 HirItem::Union(..) => ("ItemUnion", LABELS_ADT),
277
278 HirItem::Trait(..) => ("ItemTrait", LABELS_TRAIT),
280
281 HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
283
284 _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem {
285 span: attr.span(),
286 kind: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", item.kind))
})format!("{:?}", item.kind),
287 }),
288 }
289 }
290 HirNode::TraitItem(item) => match item.kind {
291 TraitItemKind::Fn(..) => ("Node::TraitItem", LABELS_FN_IN_TRAIT),
292 TraitItemKind::Const(..) => ("NodeTraitConst", LABELS_CONST_IN_TRAIT),
293 TraitItemKind::Type(..) => ("NodeTraitType", LABELS_CONST_IN_TRAIT),
294 },
295 HirNode::ImplItem(item) => match item.kind {
296 ImplItemKind::Fn(..) => ("Node::ImplItem", LABELS_FN_IN_IMPL),
297 ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL),
298 ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
299 },
300 _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirty {
301 span: attr.span(),
302 kind: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", node))
})format!("{node:?}"),
303 }),
304 };
305 let labels =
306 Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string())));
307 (name, labels)
308 }
309
310 fn resolve_labels(&self, item: &MetaItemInner, value: Symbol) -> Labels {
311 let mut out = Labels::default();
312 for label in value.as_str().split(',') {
313 let label = label.trim();
314 if DepNode::has_label_string(label) {
315 if out.contains(label) {
316 self.tcx
317 .dcx()
318 .emit_fatal(errors::RepeatedDepNodeLabel { span: item.span(), label });
319 }
320 out.insert(label.to_string());
321 } else {
322 self.tcx
323 .dcx()
324 .emit_fatal(errors::UnrecognizedDepNodeLabel { span: item.span(), label });
325 }
326 }
327 out
328 }
329
330 fn dep_node_str(&self, dep_node: &DepNode) -> String {
331 if let Some(def_id) = dep_node.extract_def_id(self.tcx) {
332 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}({1})", dep_node.kind,
self.tcx.def_path_str(def_id)))
})format!("{:?}({})", dep_node.kind, self.tcx.def_path_str(def_id))
333 } else {
334 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}({1:?})", dep_node.kind,
dep_node.hash))
})format!("{:?}({:?})", dep_node.kind, dep_node.hash)
335 }
336 }
337
338 fn assert_dirty(&self, item_span: Span, dep_node: DepNode) {
339 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/dirty_clean.rs:339",
"rustc_incremental::persist::dirty_clean",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/dirty_clean.rs"),
::tracing_core::__macro_support::Option::Some(339u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::dirty_clean"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("assert_dirty({0:?})",
dep_node) as &dyn Value))])
});
} else { ; }
};debug!("assert_dirty({:?})", dep_node);
340
341 if self.tcx.dep_graph.is_green(&dep_node) {
342 let dep_node_str = self.dep_node_str(&dep_node);
343 self.tcx
344 .dcx()
345 .emit_err(errors::NotDirty { span: item_span, dep_node_str: &dep_node_str });
346 }
347 }
348
349 fn assert_clean(&self, item_span: Span, dep_node: DepNode) {
350 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/dirty_clean.rs:350",
"rustc_incremental::persist::dirty_clean",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/dirty_clean.rs"),
::tracing_core::__macro_support::Option::Some(350u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::dirty_clean"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("assert_clean({0:?})",
dep_node) as &dyn Value))])
});
} else { ; }
};debug!("assert_clean({:?})", dep_node);
351
352 if self.tcx.dep_graph.is_red(&dep_node) {
353 let dep_node_str = self.dep_node_str(&dep_node);
354 self.tcx
355 .dcx()
356 .emit_err(errors::NotClean { span: item_span, dep_node_str: &dep_node_str });
357 }
358 }
359
360 fn check_item(&mut self, item_id: LocalDefId) {
361 let item_span = self.tcx.def_span(item_id.to_def_id());
362 let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id());
363 for attr in self.tcx.get_attrs(item_id, sym::rustc_clean) {
364 let Some(assertion) = self.assertion_maybe(item_id, attr) else {
365 continue;
366 };
367 self.checked_attrs.insert(attr.id());
368 for label in assertion.clean.items().into_sorted_stable_ord() {
369 let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
370 self.assert_clean(item_span, dep_node);
371 }
372 for label in assertion.dirty.items().into_sorted_stable_ord() {
373 let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
374 self.assert_dirty(item_span, dep_node);
375 }
376 for label in assertion.loaded_from_disk.items().into_sorted_stable_ord() {
377 match DepNode::from_label_string(self.tcx, label, def_path_hash) {
378 Ok(dep_node) => {
379 if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) {
380 let dep_node_str = self.dep_node_str(&dep_node);
381 self.tcx.dcx().emit_err(errors::NotLoaded {
382 span: item_span,
383 dep_node_str: &dep_node_str,
384 });
385 }
386 }
387 Err(()) => {
389 let dep_kind = dep_kind_from_label(label);
390 if !self.tcx.dep_graph.debug_dep_kind_was_loaded_from_disk(dep_kind) {
391 self.tcx.dcx().emit_err(errors::NotLoaded {
392 span: item_span,
393 dep_node_str: &label,
394 });
395 }
396 }
397 }
398 }
399 }
400 }
401}
402
403fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool {
406 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/dirty_clean.rs:406",
"rustc_incremental::persist::dirty_clean",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/dirty_clean.rs"),
::tracing_core::__macro_support::Option::Some(406u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::dirty_clean"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("check_config(attr={0:?})",
attr) as &dyn Value))])
});
} else { ; }
};debug!("check_config(attr={:?})", attr);
407 let config = &tcx.sess.psess.config;
408 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/dirty_clean.rs:408",
"rustc_incremental::persist::dirty_clean",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/dirty_clean.rs"),
::tracing_core::__macro_support::Option::Some(408u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::dirty_clean"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("check_config: config={0:?}",
config) as &dyn Value))])
});
} else { ; }
};debug!("check_config: config={:?}", config);
409 let mut cfg = None;
410 for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
411 if item.has_name(CFG) {
412 let value = expect_associated_value(tcx, &item);
413 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/dirty_clean.rs:413",
"rustc_incremental::persist::dirty_clean",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/dirty_clean.rs"),
::tracing_core::__macro_support::Option::Some(413u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::dirty_clean"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("check_config: searching for cfg {0:?}",
value) as &dyn Value))])
});
} else { ; }
};debug!("check_config: searching for cfg {:?}", value);
414 cfg = Some(config.contains(&(value, None)));
415 } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) {
416 tcx.dcx().emit_err(errors::UnknownRustcCleanArgument { span: item.span() });
417 }
418 }
419
420 match cfg {
421 None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span() }),
422 Some(c) => c,
423 }
424}
425
426fn expect_associated_value(tcx: TyCtxt<'_>, item: &MetaItemInner) -> Symbol {
427 if let Some(value) = item.value_str() {
428 value
429 } else if let Some(ident) = item.ident() {
430 tcx.dcx().emit_fatal(errors::AssociatedValueExpectedFor { span: item.span(), ident });
431 } else {
432 tcx.dcx().emit_fatal(errors::AssociatedValueExpected { span: item.span() });
433 }
434}
435
436struct FindAllAttrs<'tcx> {
440 tcx: TyCtxt<'tcx>,
441 found_attrs: Vec<&'tcx Attribute>,
442}
443
444impl<'tcx> FindAllAttrs<'tcx> {
445 fn is_active_attr(&mut self, attr: &Attribute) -> bool {
446 if attr.has_name(sym::rustc_clean) && check_config(self.tcx, attr) {
447 return true;
448 }
449
450 false
451 }
452
453 fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<ast::AttrId>) {
454 for attr in &self.found_attrs {
455 if !checked_attrs.contains(&attr.id()) {
456 self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span() });
457 checked_attrs.insert(attr.id());
458 }
459 }
460 }
461}
462
463impl<'tcx> intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> {
464 type NestedFilter = nested_filter::All;
465
466 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
467 self.tcx
468 }
469
470 fn visit_attribute(&mut self, attr: &'tcx Attribute) {
471 if self.is_active_attr(attr) {
472 self.found_attrs.push(attr);
473 }
474 }
475}