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