]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_incremental/src/persist/dirty_clean.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_incremental / src / persist / dirty_clean.rs
CommitLineData
9c376795 1//! Debugging code to test fingerprints computed for query results. For each node marked with
17df50a5 2//! `#[rustc_clean]` we will compare the fingerprint from the current and from the previous
ea8adc8c 3//! compilation session as appropriate:
54a0048b 4//!
3dfed10e 5//! - `#[rustc_clean(cfg="rev2", except="typeck")]` if we are
ea8adc8c 6//! in `#[cfg(rev2)]`, then the fingerprints associated with
3dfed10e 7//! `DepNode::typeck(X)` must be DIFFERENT (`X` is the `DefId` of the
ea8adc8c 8//! current node).
abe05a73
XL
9//! - `#[rustc_clean(cfg="rev2")]` same as above, except that the
10//! fingerprints must be the SAME (along with all other fingerprints).
54a0048b 11//!
a2a8927a
XL
12//! - `#[rustc_clean(cfg="rev2", loaded_from_disk='typeck")]` asserts that
13//! the query result for `DepNode::typeck(X)` was actually
14//! loaded from disk (not just marked green). This can be useful
15//! to ensure that a test is actually exercising the deserialization
16//! logic for a particular query result. This can be combined with
17//! `except`
18//!
54a0048b
SL
19//! Errors are reported if we are in the suitable configuration but
20//! the required condition is not met.
21
9ffffee4 22use crate::errors;
3dfed10e 23use rustc_ast::{self as ast, Attribute, NestedMetaItem};
ff7c6d11 24use rustc_data_structures::fx::FxHashSet;
3c0e092e 25use rustc_hir::def_id::LocalDefId;
dfeec247 26use rustc_hir::intravisit;
dfeec247
XL
27use rustc_hir::Node as HirNode;
28use rustc_hir::{ImplItemKind, ItemKind as HirItem, TraitItemKind};
ba9703b0 29use rustc_middle::dep_graph::{label_strs, DepNode, DepNodeExt};
5099ac24 30use rustc_middle::hir::nested_filter;
ba9703b0 31use rustc_middle::ty::TyCtxt;
dfeec247
XL
32use rustc_span::symbol::{sym, Symbol};
33use rustc_span::Span;
9ffffee4
FG
34use std::iter::FromIterator;
35use thin_vec::ThinVec;
9e0c209e 36
a2a8927a 37const LOADED_FROM_DISK: Symbol = sym::loaded_from_disk;
48663c56 38const EXCEPT: Symbol = sym::except;
48663c56 39const CFG: Symbol = sym::cfg;
abe05a73
XL
40
41// Base and Extra labels to build up the labels
42
43/// For typedef, constants, and statics
dfeec247 44const BASE_CONST: &[&str] = &[label_strs::type_of];
abe05a73
XL
45
46/// DepNodes for functions + methods
47const BASE_FN: &[&str] = &[
48 // Callers will depend on the signature of these items, so we better test
532ac7d7
XL
49 label_strs::fn_sig,
50 label_strs::generics_of,
51 label_strs::predicates_of,
52 label_strs::type_of,
abe05a73
XL
53 // And a big part of compilation (that we eventually want to cache) is type inference
54 // information:
3dfed10e 55 label_strs::typeck,
abe05a73
XL
56];
57
58/// DepNodes for Hir, which is pretty much everything
59const BASE_HIR: &[&str] = &[
ba9703b0
XL
60 // hir_owner and hir_owner_nodes should be computed for all nodes
61 label_strs::hir_owner,
62 label_strs::hir_owner_nodes,
abe05a73
XL
63];
64
65/// `impl` implementation of struct/trait
dfeec247
XL
66const BASE_IMPL: &[&str] =
67 &[label_strs::associated_item_def_ids, label_strs::generics_of, label_strs::impl_trait_ref];
abe05a73 68
532ac7d7 69/// DepNodes for mir_built/Optimized, which is relevant in "executable"
0731742a 70/// code, i.e., functions+methods
3dfed10e 71const BASE_MIR: &[&str] = &[label_strs::optimized_mir, label_strs::promoted_mir];
abe05a73
XL
72
73/// Struct, Enum and Union DepNodes
74///
75/// Note that changing the type of a field does not change the type of the struct or enum, but
76/// adding/removing fields or changing a fields name or visibility does.
dfeec247
XL
77const BASE_STRUCT: &[&str] =
78 &[label_strs::generics_of, label_strs::predicates_of, label_strs::type_of];
abe05a73 79
9fa01778 80/// Trait definition `DepNode`s.
9fa01778 81/// Extra `DepNode`s for functions and methods.
dfeec247 82const EXTRA_ASSOCIATED: &[&str] = &[label_strs::associated_item];
abe05a73 83
064997fb 84const EXTRA_TRAIT: &[&str] = &[];
abe05a73
XL
85
86// Fully Built Labels
87
dfeec247 88const LABELS_CONST: &[&[&str]] = &[BASE_HIR, BASE_CONST];
abe05a73
XL
89
90/// Constant/Typedef in an impl
dfeec247 91const LABELS_CONST_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED];
abe05a73
XL
92
93/// Trait-Const/Typedef DepNodes
dfeec247 94const LABELS_CONST_IN_TRAIT: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED, EXTRA_TRAIT];
abe05a73 95
9fa01778 96/// Function `DepNode`s.
dfeec247 97const LABELS_FN: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN];
abe05a73 98
9fa01778 99/// Method `DepNode`s.
dfeec247 100const LABELS_FN_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED];
abe05a73 101
9fa01778 102/// Trait method `DepNode`s.
dfeec247
XL
103const LABELS_FN_IN_TRAIT: &[&[&str]] =
104 &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED, EXTRA_TRAIT];
abe05a73 105
9fa01778 106/// For generic cases like inline-assembly, modules, etc.
dfeec247 107const LABELS_HIR_ONLY: &[&[&str]] = &[BASE_HIR];
abe05a73 108
17df50a5
XL
109/// Impl `DepNode`s.
110const LABELS_TRAIT: &[&[&str]] = &[
111 BASE_HIR,
112 &[label_strs::associated_item_def_ids, label_strs::predicates_of, label_strs::generics_of],
113];
114
9fa01778 115/// Impl `DepNode`s.
dfeec247 116const LABELS_IMPL: &[&[&str]] = &[BASE_HIR, BASE_IMPL];
abe05a73 117
9fa01778 118/// Abstract data type (struct, enum, union) `DepNode`s.
dfeec247 119const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT];
abe05a73 120
abe05a73
XL
121// FIXME: Struct/Enum/Unions Fields (there is currently no way to attach these)
122//
123// Fields are kind of separate from their containers, as they can change independently from
124// them. We should at least check
125//
532ac7d7 126// type_of for these.
54a0048b 127
b7449926 128type Labels = FxHashSet<String>;
ea8adc8c 129
abe05a73
XL
130/// Represents the requested configuration by rustc_clean/dirty
131struct Assertion {
132 clean: Labels,
133 dirty: Labels,
a2a8927a 134 loaded_from_disk: Labels,
abe05a73
XL
135}
136
416331ca 137pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) {
064997fb 138 if !tcx.sess.opts.unstable_opts.query_dep_graph {
cdc7bbd5
XL
139 return;
140 }
141
17df50a5 142 // can't add `#[rustc_clean]` etc without opting in to this feature
0531ce1d 143 if !tcx.features().rustc_attrs {
5bcae85e
SL
144 return;
145 }
146
2c00a5a8 147 tcx.dep_graph.with_ignore(|| {
dfeec247 148 let mut dirty_clean_visitor = DirtyCleanVisitor { tcx, checked_attrs: Default::default() };
04454e1e
FG
149
150 let crate_items = tcx.hir_crate_items(());
151
152 for id in crate_items.items() {
2b03887a 153 dirty_clean_visitor.check_item(id.owner_id.def_id);
04454e1e
FG
154 }
155
156 for id in crate_items.trait_items() {
2b03887a 157 dirty_clean_visitor.check_item(id.owner_id.def_id);
04454e1e
FG
158 }
159
160 for id in crate_items.impl_items() {
2b03887a 161 dirty_clean_visitor.check_item(id.owner_id.def_id);
04454e1e
FG
162 }
163
164 for id in crate_items.foreign_items() {
2b03887a 165 dirty_clean_visitor.check_item(id.owner_id.def_id);
04454e1e 166 }
2c00a5a8 167
17df50a5 168 let mut all_attrs = FindAllAttrs { tcx, found_attrs: vec![] };
c295e0f8 169 tcx.hir().walk_attributes(&mut all_attrs);
2c00a5a8
XL
170
171 // Note that we cannot use the existing "unused attribute"-infrastructure
94b46f34 172 // here, since that is running before codegen. This is also the reason why
f035d41b 173 // all codegen-specific attributes are `AssumedUsed` in rustc_ast::feature_gate.
6a06907d 174 all_attrs.report_unchecked_attrs(dirty_clean_visitor.checked_attrs);
2c00a5a8 175 })
54a0048b
SL
176}
177
dc9dc135
XL
178pub struct DirtyCleanVisitor<'tcx> {
179 tcx: TyCtxt<'tcx>,
8bb4bdeb 180 checked_attrs: FxHashSet<ast::AttrId>,
54a0048b
SL
181}
182
a2a8927a 183impl<'tcx> DirtyCleanVisitor<'tcx> {
abe05a73 184 /// Possibly "deserialize" the attribute into a clean/dirty assertion
6a06907d 185 fn assertion_maybe(&mut self, item_id: LocalDefId, attr: &Attribute) -> Option<Assertion> {
04454e1e 186 assert!(attr.has_name(sym::rustc_clean));
abe05a73
XL
187 if !check_config(self.tcx, attr) {
188 // skip: not the correct `cfg=`
189 return None;
190 }
17df50a5 191 let assertion = self.assertion_auto(item_id, attr);
abe05a73
XL
192 Some(assertion)
193 }
194
9fa01778 195 /// Gets the "auto" assertion on pre-validated attr, along with the `except` labels.
17df50a5 196 fn assertion_auto(&mut self, item_id: LocalDefId, attr: &Attribute) -> Assertion {
abe05a73
XL
197 let (name, mut auto) = self.auto_labels(item_id, attr);
198 let except = self.except(attr);
a2a8927a 199 let loaded_from_disk = self.loaded_from_disk(attr);
abe05a73
XL
200 for e in except.iter() {
201 if !auto.remove(e) {
9ffffee4 202 self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e });
abe05a73
XL
203 }
204 }
a2a8927a
XL
205 Assertion { clean: auto, dirty: except, loaded_from_disk }
206 }
207
208 /// `loaded_from_disk=` attribute value
209 fn loaded_from_disk(&self, attr: &Attribute) -> Labels {
9ffffee4 210 for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
a2a8927a
XL
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 // If `loaded_from_disk=` is not specified, don't assert anything
217 Labels::default()
abe05a73
XL
218 }
219
220 /// `except=` attribute value
221 fn except(&self, attr: &Attribute) -> Labels {
9ffffee4 222 for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
3dfed10e 223 if item.has_name(EXCEPT) {
cc61c64b 224 let value = expect_associated_value(self.tcx, &item);
3dfed10e 225 return self.resolve_labels(&item, value);
ea8adc8c
XL
226 }
227 }
abe05a73 228 // if no `label` or `except` is given, only the node's group are asserted
b7449926 229 Labels::default()
abe05a73
XL
230 }
231
232 /// Return all DepNode labels that should be asserted for this item.
233 /// index=0 is the "name" used for error messages
6a06907d 234 fn auto_labels(&mut self, item_id: LocalDefId, attr: &Attribute) -> (&'static str, Labels) {
5099ac24 235 let node = self.tcx.hir().get_by_def_id(item_id);
abe05a73 236 let (name, labels) = match node {
b7449926 237 HirNode::Item(item) => {
e74abb32 238 match item.kind {
abe05a73
XL
239 // note: these are in the same order as hir::Item_;
240 // FIXME(michaelwoerister): do commented out ones
241
242 // // An `extern crate` item, with optional original crate name,
8faf50e0 243 // HirItem::ExternCrate(..), // intentionally no assertions
abe05a73
XL
244
245 // // `use foo::bar::*;` or `use foo::bar::baz as quux;`
8faf50e0 246 // HirItem::Use(..), // intentionally no assertions
abe05a73
XL
247
248 // A `static` item
8faf50e0 249 HirItem::Static(..) => ("ItemStatic", LABELS_CONST),
abe05a73
XL
250
251 // A `const` item
8faf50e0 252 HirItem::Const(..) => ("ItemConst", LABELS_CONST),
abe05a73
XL
253
254 // A function declaration
8faf50e0 255 HirItem::Fn(..) => ("ItemFn", LABELS_FN),
abe05a73
XL
256
257 // // A module
dfeec247 258 HirItem::Mod(..) => ("ItemMod", LABELS_HIR_ONLY),
abe05a73
XL
259
260 // // An external module
fc512014 261 HirItem::ForeignMod { .. } => ("ItemForeignMod", LABELS_HIR_ONLY),
abe05a73
XL
262
263 // Module-level inline assembly (from global_asm!)
8faf50e0 264 HirItem::GlobalAsm(..) => ("ItemGlobalAsm", LABELS_HIR_ONLY),
abe05a73 265
0731742a 266 // A type alias, e.g., `type Foo = Bar<u8>`
416331ca 267 HirItem::TyAlias(..) => ("ItemTy", LABELS_HIR_ONLY),
abe05a73 268
0731742a 269 // An enum definition, e.g., `enum Foo<A, B> {C<A>, D<B>}`
8faf50e0 270 HirItem::Enum(..) => ("ItemEnum", LABELS_ADT),
abe05a73 271
0731742a 272 // A struct definition, e.g., `struct Foo<A> {x: A}`
8faf50e0 273 HirItem::Struct(..) => ("ItemStruct", LABELS_ADT),
abe05a73 274
0731742a 275 // A union definition, e.g., `union Foo<A, B> {x: A, y: B}`
8faf50e0 276 HirItem::Union(..) => ("ItemUnion", LABELS_ADT),
abe05a73
XL
277
278 // Represents a Trait Declaration
17df50a5 279 HirItem::Trait(..) => ("ItemTrait", LABELS_TRAIT),
abe05a73 280
abe05a73 281 // An implementation, eg `impl<A> Trait for Foo { .. }`
dfeec247 282 HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
abe05a73 283
9ffffee4
FG
284 _ => self.tcx.sess.emit_fatal(errors::UndefinedCleanDirtyItem {
285 span: attr.span,
286 kind: format!("{:?}", item.kind),
287 }),
abe05a73 288 }
dfeec247
XL
289 }
290 HirNode::TraitItem(item) => match item.kind {
ba9703b0 291 TraitItemKind::Fn(..) => ("Node::TraitItem", LABELS_FN_IN_TRAIT),
dfeec247
XL
292 TraitItemKind::Const(..) => ("NodeTraitConst", LABELS_CONST_IN_TRAIT),
293 TraitItemKind::Type(..) => ("NodeTraitType", LABELS_CONST_IN_TRAIT),
abe05a73 294 },
dfeec247 295 HirNode::ImplItem(item) => match item.kind {
ba9703b0 296 ImplItemKind::Fn(..) => ("Node::ImplItem", LABELS_FN_IN_IMPL),
dfeec247 297 ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL),
2b03887a 298 ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
abe05a73 299 },
9ffffee4
FG
300 _ => self.tcx.sess.emit_fatal(errors::UndefinedCleanDirty {
301 span: attr.span,
302 kind: format!("{:?}", node),
303 }),
abe05a73 304 };
74b04a01
XL
305 let labels =
306 Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string())));
abe05a73 307 (name, labels)
ea8adc8c
XL
308 }
309
3dfed10e 310 fn resolve_labels(&self, item: &NestedMetaItem, value: Symbol) -> Labels {
b7449926 311 let mut out = Labels::default();
3dfed10e 312 for label in value.as_str().split(',') {
ea8adc8c
XL
313 let label = label.trim();
314 if DepNode::has_label_string(label) {
315 if out.contains(label) {
9ffffee4
FG
316 self.tcx
317 .sess
318 .emit_fatal(errors::RepeatedDepNodeLabel { span: item.span(), label });
54a0048b 319 }
ea8adc8c
XL
320 out.insert(label.to_string());
321 } else {
dfeec247
XL
322 self.tcx
323 .sess
9ffffee4 324 .emit_fatal(errors::UnrecognizedDepNodeLabel { span: item.span(), label });
54a0048b
SL
325 }
326 }
ea8adc8c
XL
327 out
328 }
54a0048b 329
041b39d2
XL
330 fn dep_node_str(&self, dep_node: &DepNode) -> String {
331 if let Some(def_id) = dep_node.extract_def_id(self.tcx) {
dfeec247 332 format!("{:?}({})", dep_node.kind, self.tcx.def_path_str(def_id))
041b39d2
XL
333 } else {
334 format!("{:?}({:?})", dep_node.kind, dep_node.hash)
335 }
54a0048b
SL
336 }
337
041b39d2 338 fn assert_dirty(&self, item_span: Span, dep_node: DepNode) {
54a0048b
SL
339 debug!("assert_dirty({:?})", dep_node);
340
cdc7bbd5 341 if self.tcx.dep_graph.is_green(&dep_node) {
ea8adc8c 342 let dep_node_str = self.dep_node_str(&dep_node);
dfeec247
XL
343 self.tcx
344 .sess
9ffffee4 345 .emit_err(errors::NotDirty { span: item_span, dep_node_str: &dep_node_str });
54a0048b
SL
346 }
347 }
348
041b39d2 349 fn assert_clean(&self, item_span: Span, dep_node: DepNode) {
54a0048b
SL
350 debug!("assert_clean({:?})", dep_node);
351
cdc7bbd5 352 if self.tcx.dep_graph.is_red(&dep_node) {
ea8adc8c 353 let dep_node_str = self.dep_node_str(&dep_node);
dfeec247
XL
354 self.tcx
355 .sess
9ffffee4 356 .emit_err(errors::NotClean { span: item_span, dep_node_str: &dep_node_str });
54a0048b
SL
357 }
358 }
54a0048b 359
a2a8927a
XL
360 fn assert_loaded_from_disk(&self, item_span: Span, dep_node: DepNode) {
361 debug!("assert_loaded_from_disk({:?})", dep_node);
362
363 if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) {
364 let dep_node_str = self.dep_node_str(&dep_node);
9ffffee4
FG
365 self.tcx
366 .sess
367 .emit_err(errors::NotLoaded { span: item_span, dep_node_str: &dep_node_str });
a2a8927a
XL
368 }
369 }
370
04454e1e
FG
371 fn check_item(&mut self, item_id: LocalDefId) {
372 let item_span = self.tcx.def_span(item_id.to_def_id());
3c0e092e 373 let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id());
353b0b11 374 for attr in self.tcx.get_attrs(item_id, sym::rustc_clean) {
5e7ed085
FG
375 let Some(assertion) = self.assertion_maybe(item_id, attr) else {
376 continue;
abe05a73
XL
377 };
378 self.checked_attrs.insert(attr.id);
3c0e092e
XL
379 for label in assertion.clean {
380 let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
abe05a73
XL
381 self.assert_clean(item_span, dep_node);
382 }
3c0e092e
XL
383 for label in assertion.dirty {
384 let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
abe05a73 385 self.assert_dirty(item_span, dep_node);
54a0048b 386 }
a2a8927a
XL
387 for label in assertion.loaded_from_disk {
388 let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
389 self.assert_loaded_from_disk(item_span, dep_node);
390 }
54a0048b
SL
391 }
392 }
8bb4bdeb
XL
393}
394
17df50a5
XL
395/// Given a `#[rustc_clean]` attribute, scan for a `cfg="foo"` attribute and check whether we have
396/// a cfg flag called `foo`.
dc9dc135 397fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool {
9e0c209e 398 debug!("check_config(attr={:?})", attr);
c30ab7b3 399 let config = &tcx.sess.parse_sess.config;
9e0c209e 400 debug!("check_config: config={:?}", config);
17df50a5 401 let mut cfg = None;
9ffffee4 402 for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
3dfed10e 403 if item.has_name(CFG) {
cc61c64b 404 let value = expect_associated_value(tcx, &item);
9e0c209e 405 debug!("check_config: searching for cfg {:?}", value);
abe05a73 406 cfg = Some(config.contains(&(value, None)));
a2a8927a 407 } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) {
9ffffee4 408 tcx.sess.emit_err(errors::UnknownItem { span: attr.span, name: item.name_or_empty() });
9e0c209e
SL
409 }
410 }
411
abe05a73 412 match cfg {
9ffffee4 413 None => tcx.sess.emit_fatal(errors::NoCfg { span: attr.span }),
abe05a73
XL
414 Some(c) => c,
415 }
9e0c209e
SL
416}
417
f9f354fc 418fn expect_associated_value(tcx: TyCtxt<'_>, item: &NestedMetaItem) -> Symbol {
9e0c209e
SL
419 if let Some(value) = item.value_str() {
420 value
421 } else {
9ffffee4
FG
422 if let Some(ident) = item.ident() {
423 tcx.sess.emit_fatal(errors::AssociatedValueExpectedFor { span: item.span(), ident });
9e0c209e 424 } else {
9ffffee4
FG
425 tcx.sess.emit_fatal(errors::AssociatedValueExpected { span: item.span() });
426 }
9e0c209e
SL
427 }
428}
8bb4bdeb 429
487cf647
FG
430/// A visitor that collects all `#[rustc_clean]` attributes from
431/// the HIR. It is used to verify that we really ran checks for all annotated
432/// nodes.
17df50a5 433pub struct FindAllAttrs<'tcx> {
dc9dc135 434 tcx: TyCtxt<'tcx>,
8bb4bdeb
XL
435 found_attrs: Vec<&'tcx Attribute>,
436}
437
a2a8927a 438impl<'tcx> FindAllAttrs<'tcx> {
8bb4bdeb 439 fn is_active_attr(&mut self, attr: &Attribute) -> bool {
94222f64 440 if attr.has_name(sym::rustc_clean) && check_config(self.tcx, attr) {
17df50a5 441 return true;
8bb4bdeb
XL
442 }
443
444 false
445 }
446
6a06907d 447 fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<ast::AttrId>) {
8bb4bdeb
XL
448 for attr in &self.found_attrs {
449 if !checked_attrs.contains(&attr.id) {
9ffffee4 450 self.tcx.sess.emit_err(errors::UncheckedClean { span: attr.span });
6a06907d 451 checked_attrs.insert(attr.id);
8bb4bdeb
XL
452 }
453 }
454 }
455}
456
a2a8927a 457impl<'tcx> intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> {
5099ac24 458 type NestedFilter = nested_filter::All;
dfeec247 459
5099ac24
FG
460 fn nested_visit_map(&mut self) -> Self::Map {
461 self.tcx.hir()
8bb4bdeb
XL
462 }
463
923072b8 464 fn visit_attribute(&mut self, attr: &'tcx Attribute) {
8bb4bdeb
XL
465 if self.is_active_attr(attr) {
466 self.found_attrs.push(attr);
467 }
468 }
469}