]>
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}; |
ff7c6d11 | 17 | use rustc_data_structures::fx::FxHashSet; |
dfeec247 | 18 | use rustc_hir as hir; |
6a06907d | 19 | use rustc_hir::def_id::{DefId, LocalDefId}; |
dfeec247 XL |
20 | use rustc_hir::intravisit; |
21 | use rustc_hir::itemlikevisit::ItemLikeVisitor; | |
22 | use rustc_hir::Node as HirNode; | |
23 | use rustc_hir::{ImplItemKind, ItemKind as HirItem, TraitItemKind}; | |
ba9703b0 XL |
24 | use rustc_middle::dep_graph::{label_strs, DepNode, DepNodeExt}; |
25 | use rustc_middle::hir::map::Map; | |
26 | use rustc_middle::ty::TyCtxt; | |
dfeec247 XL |
27 | use rustc_span::symbol::{sym, Symbol}; |
28 | use rustc_span::Span; | |
29 | use std::iter::FromIterator; | |
30 | use std::vec::Vec; | |
9e0c209e | 31 | |
48663c56 XL |
32 | const EXCEPT: Symbol = sym::except; |
33 | const LABEL: Symbol = sym::label; | |
34 | const 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 | 39 | const BASE_CONST: &[&str] = &[label_strs::type_of]; |
abe05a73 XL |
40 | |
41 | /// DepNodes for functions + methods | |
42 | const 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 | |
54 | const 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 |
61 | const 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 | 66 | const 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 |
72 | const 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 | 77 | const EXTRA_ASSOCIATED: &[&str] = &[label_strs::associated_item]; |
abe05a73 | 78 | |
dfeec247 | 79 | const EXTRA_TRAIT: &[&str] = &[label_strs::trait_of_item]; |
abe05a73 XL |
80 | |
81 | // Fully Built Labels | |
82 | ||
dfeec247 | 83 | const LABELS_CONST: &[&[&str]] = &[BASE_HIR, BASE_CONST]; |
abe05a73 XL |
84 | |
85 | /// Constant/Typedef in an impl | |
dfeec247 | 86 | const LABELS_CONST_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED]; |
abe05a73 XL |
87 | |
88 | /// Trait-Const/Typedef DepNodes | |
dfeec247 | 89 | const LABELS_CONST_IN_TRAIT: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED, EXTRA_TRAIT]; |
abe05a73 | 90 | |
9fa01778 | 91 | /// Function `DepNode`s. |
dfeec247 | 92 | const LABELS_FN: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN]; |
abe05a73 | 93 | |
9fa01778 | 94 | /// Method `DepNode`s. |
dfeec247 | 95 | const LABELS_FN_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED]; |
abe05a73 | 96 | |
9fa01778 | 97 | /// Trait method `DepNode`s. |
dfeec247 XL |
98 | const 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 | 102 | const LABELS_HIR_ONLY: &[&[&str]] = &[BASE_HIR]; |
abe05a73 | 103 | |
9fa01778 | 104 | /// Impl `DepNode`s. |
dfeec247 | 105 | const LABELS_IMPL: &[&[&str]] = &[BASE_HIR, BASE_IMPL]; |
abe05a73 | 106 | |
9fa01778 | 107 | /// Abstract data type (struct, enum, union) `DepNode`s. |
dfeec247 | 108 | const 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 | 117 | type Labels = FxHashSet<String>; |
ea8adc8c | 118 | |
abe05a73 XL |
119 | /// Represents the requested configuration by rustc_clean/dirty |
120 | struct Assertion { | |
121 | clean: Labels, | |
122 | dirty: Labels, | |
123 | } | |
124 | ||
125 | impl 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 | 135 | pub 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 |
164 | pub struct DirtyCleanVisitor<'tcx> { |
165 | tcx: TyCtxt<'tcx>, | |
8bb4bdeb | 166 | checked_attrs: FxHashSet<ast::AttrId>, |
54a0048b SL |
167 | } |
168 | ||
dc9dc135 | 169 | impl 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 | 419 | impl 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 | 443 | fn 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 | 472 | fn 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 | 489 | pub 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 | 495 | impl 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 | 519 | impl 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 | } |