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