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