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