]>
Commit | Line | Data |
---|---|---|
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 | 16 | use rustc_ast::{self as ast, Attribute, NestedMetaItem}; |
e1599b0c | 17 | use rustc_data_structures::fingerprint::Fingerprint; |
ff7c6d11 | 18 | use rustc_data_structures::fx::FxHashSet; |
dfeec247 | 19 | use rustc_hir as hir; |
6a06907d | 20 | use rustc_hir::def_id::{DefId, LocalDefId}; |
dfeec247 XL |
21 | use rustc_hir::intravisit; |
22 | use rustc_hir::itemlikevisit::ItemLikeVisitor; | |
23 | use rustc_hir::Node as HirNode; | |
24 | use rustc_hir::{ImplItemKind, ItemKind as HirItem, TraitItemKind}; | |
ba9703b0 XL |
25 | use rustc_middle::dep_graph::{label_strs, DepNode, DepNodeExt}; |
26 | use rustc_middle::hir::map::Map; | |
27 | use rustc_middle::ty::TyCtxt; | |
dfeec247 XL |
28 | use rustc_span::symbol::{sym, Symbol}; |
29 | use rustc_span::Span; | |
30 | use std::iter::FromIterator; | |
31 | use std::vec::Vec; | |
9e0c209e | 32 | |
48663c56 XL |
33 | const EXCEPT: Symbol = sym::except; |
34 | const LABEL: Symbol = sym::label; | |
35 | const 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 | 40 | const BASE_CONST: &[&str] = &[label_strs::type_of]; |
abe05a73 XL |
41 | |
42 | /// DepNodes for functions + methods | |
43 | const 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 | |
55 | const 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 |
62 | const 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 | 67 | const 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 |
73 | const 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 | 77 | const 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 | 88 | const EXTRA_ASSOCIATED: &[&str] = &[label_strs::associated_item]; |
abe05a73 | 89 | |
dfeec247 | 90 | const EXTRA_TRAIT: &[&str] = &[label_strs::trait_of_item]; |
abe05a73 XL |
91 | |
92 | // Fully Built Labels | |
93 | ||
dfeec247 | 94 | const LABELS_CONST: &[&[&str]] = &[BASE_HIR, BASE_CONST]; |
abe05a73 XL |
95 | |
96 | /// Constant/Typedef in an impl | |
dfeec247 | 97 | const LABELS_CONST_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED]; |
abe05a73 XL |
98 | |
99 | /// Trait-Const/Typedef DepNodes | |
dfeec247 | 100 | const LABELS_CONST_IN_TRAIT: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED, EXTRA_TRAIT]; |
abe05a73 | 101 | |
9fa01778 | 102 | /// Function `DepNode`s. |
dfeec247 | 103 | const LABELS_FN: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN]; |
abe05a73 | 104 | |
9fa01778 | 105 | /// Method `DepNode`s. |
dfeec247 | 106 | const LABELS_FN_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED]; |
abe05a73 | 107 | |
9fa01778 | 108 | /// Trait method `DepNode`s. |
dfeec247 XL |
109 | const 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 | 113 | const LABELS_HIR_ONLY: &[&[&str]] = &[BASE_HIR]; |
abe05a73 | 114 | |
9fa01778 | 115 | /// Impl `DepNode`s. |
dfeec247 | 116 | const LABELS_IMPL: &[&[&str]] = &[BASE_HIR, BASE_IMPL]; |
abe05a73 | 117 | |
9fa01778 | 118 | /// Abstract data type (struct, enum, union) `DepNode`s. |
dfeec247 | 119 | const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT]; |
abe05a73 | 120 | |
9fa01778 | 121 | /// Trait definition `DepNode`s. |
abe05a73 | 122 | #[allow(dead_code)] |
dfeec247 | 123 | const 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 | 132 | type Labels = FxHashSet<String>; |
ea8adc8c | 133 | |
abe05a73 XL |
134 | /// Represents the requested configuration by rustc_clean/dirty |
135 | struct Assertion { | |
136 | clean: Labels, | |
137 | dirty: Labels, | |
138 | } | |
139 | ||
140 | impl 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 | 150 | pub 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 |
175 | pub struct DirtyCleanVisitor<'tcx> { |
176 | tcx: TyCtxt<'tcx>, | |
8bb4bdeb | 177 | checked_attrs: FxHashSet<ast::AttrId>, |
54a0048b SL |
178 | } |
179 | ||
dc9dc135 | 180 | impl 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 | 451 | impl 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 | 475 | fn 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 | 504 | fn 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 | 521 | pub 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 | 527 | impl 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 | 551 | impl 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 | } |