]>
Commit | Line | Data |
---|---|---|
9c376795 | 1 | //! Debugging code to test fingerprints computed for query results. For each node marked with |
17df50a5 | 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 | ||
9ffffee4 | 22 | use crate::errors; |
3dfed10e | 23 | use rustc_ast::{self as ast, Attribute, NestedMetaItem}; |
ff7c6d11 | 24 | use rustc_data_structures::fx::FxHashSet; |
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; | |
9ffffee4 FG |
34 | use std::iter::FromIterator; |
35 | use thin_vec::ThinVec; | |
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 | |
064997fb | 84 | const EXTRA_TRAIT: &[&str] = &[]; |
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<'_>) { |
064997fb | 138 | if !tcx.sess.opts.unstable_opts.query_dep_graph { |
cdc7bbd5 XL |
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() { | |
2b03887a | 153 | dirty_clean_visitor.check_item(id.owner_id.def_id); |
04454e1e FG |
154 | } |
155 | ||
156 | for id in crate_items.trait_items() { | |
2b03887a | 157 | dirty_clean_visitor.check_item(id.owner_id.def_id); |
04454e1e FG |
158 | } |
159 | ||
160 | for id in crate_items.impl_items() { | |
2b03887a | 161 | dirty_clean_visitor.check_item(id.owner_id.def_id); |
04454e1e FG |
162 | } |
163 | ||
164 | for id in crate_items.foreign_items() { | |
2b03887a | 165 | dirty_clean_visitor.check_item(id.owner_id.def_id); |
04454e1e | 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) { | |
9ffffee4 | 202 | self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); |
abe05a73 XL |
203 | } |
204 | } | |
a2a8927a XL |
205 | Assertion { clean: auto, dirty: except, loaded_from_disk } |
206 | } | |
207 | ||
208 | /// `loaded_from_disk=` attribute value | |
209 | fn loaded_from_disk(&self, attr: &Attribute) -> Labels { | |
9ffffee4 | 210 | for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { |
a2a8927a XL |
211 | if item.has_name(LOADED_FROM_DISK) { |
212 | let value = expect_associated_value(self.tcx, &item); | |
213 | return self.resolve_labels(&item, value); | |
214 | } | |
215 | } | |
216 | // If `loaded_from_disk=` is not specified, don't assert anything | |
217 | Labels::default() | |
abe05a73 XL |
218 | } |
219 | ||
220 | /// `except=` attribute value | |
221 | fn except(&self, attr: &Attribute) -> Labels { | |
9ffffee4 | 222 | for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { |
3dfed10e | 223 | if item.has_name(EXCEPT) { |
cc61c64b | 224 | let value = expect_associated_value(self.tcx, &item); |
3dfed10e | 225 | return self.resolve_labels(&item, value); |
ea8adc8c XL |
226 | } |
227 | } | |
abe05a73 | 228 | // if no `label` or `except` is given, only the node's group are asserted |
b7449926 | 229 | Labels::default() |
abe05a73 XL |
230 | } |
231 | ||
232 | /// Return all DepNode labels that should be asserted for this item. | |
233 | /// index=0 is the "name" used for error messages | |
6a06907d | 234 | fn auto_labels(&mut self, item_id: LocalDefId, attr: &Attribute) -> (&'static str, Labels) { |
5099ac24 | 235 | let node = self.tcx.hir().get_by_def_id(item_id); |
abe05a73 | 236 | let (name, labels) = match node { |
b7449926 | 237 | HirNode::Item(item) => { |
e74abb32 | 238 | match item.kind { |
abe05a73 XL |
239 | // note: these are in the same order as hir::Item_; |
240 | // FIXME(michaelwoerister): do commented out ones | |
241 | ||
242 | // // An `extern crate` item, with optional original crate name, | |
8faf50e0 | 243 | // HirItem::ExternCrate(..), // intentionally no assertions |
abe05a73 XL |
244 | |
245 | // // `use foo::bar::*;` or `use foo::bar::baz as quux;` | |
8faf50e0 | 246 | // HirItem::Use(..), // intentionally no assertions |
abe05a73 XL |
247 | |
248 | // A `static` item | |
8faf50e0 | 249 | HirItem::Static(..) => ("ItemStatic", LABELS_CONST), |
abe05a73 XL |
250 | |
251 | // A `const` item | |
8faf50e0 | 252 | HirItem::Const(..) => ("ItemConst", LABELS_CONST), |
abe05a73 XL |
253 | |
254 | // A function declaration | |
8faf50e0 | 255 | HirItem::Fn(..) => ("ItemFn", LABELS_FN), |
abe05a73 XL |
256 | |
257 | // // A module | |
dfeec247 | 258 | HirItem::Mod(..) => ("ItemMod", LABELS_HIR_ONLY), |
abe05a73 XL |
259 | |
260 | // // An external module | |
fc512014 | 261 | HirItem::ForeignMod { .. } => ("ItemForeignMod", LABELS_HIR_ONLY), |
abe05a73 XL |
262 | |
263 | // Module-level inline assembly (from global_asm!) | |
8faf50e0 | 264 | HirItem::GlobalAsm(..) => ("ItemGlobalAsm", LABELS_HIR_ONLY), |
abe05a73 | 265 | |
0731742a | 266 | // A type alias, e.g., `type Foo = Bar<u8>` |
416331ca | 267 | HirItem::TyAlias(..) => ("ItemTy", LABELS_HIR_ONLY), |
abe05a73 | 268 | |
0731742a | 269 | // An enum definition, e.g., `enum Foo<A, B> {C<A>, D<B>}` |
8faf50e0 | 270 | HirItem::Enum(..) => ("ItemEnum", LABELS_ADT), |
abe05a73 | 271 | |
0731742a | 272 | // A struct definition, e.g., `struct Foo<A> {x: A}` |
8faf50e0 | 273 | HirItem::Struct(..) => ("ItemStruct", LABELS_ADT), |
abe05a73 | 274 | |
0731742a | 275 | // A union definition, e.g., `union Foo<A, B> {x: A, y: B}` |
8faf50e0 | 276 | HirItem::Union(..) => ("ItemUnion", LABELS_ADT), |
abe05a73 XL |
277 | |
278 | // Represents a Trait Declaration | |
17df50a5 | 279 | HirItem::Trait(..) => ("ItemTrait", LABELS_TRAIT), |
abe05a73 | 280 | |
abe05a73 | 281 | // An implementation, eg `impl<A> Trait for Foo { .. }` |
dfeec247 | 282 | HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL), |
abe05a73 | 283 | |
9ffffee4 FG |
284 | _ => self.tcx.sess.emit_fatal(errors::UndefinedCleanDirtyItem { |
285 | span: attr.span, | |
286 | kind: format!("{:?}", item.kind), | |
287 | }), | |
abe05a73 | 288 | } |
dfeec247 XL |
289 | } |
290 | HirNode::TraitItem(item) => match item.kind { | |
ba9703b0 | 291 | TraitItemKind::Fn(..) => ("Node::TraitItem", LABELS_FN_IN_TRAIT), |
dfeec247 XL |
292 | TraitItemKind::Const(..) => ("NodeTraitConst", LABELS_CONST_IN_TRAIT), |
293 | TraitItemKind::Type(..) => ("NodeTraitType", LABELS_CONST_IN_TRAIT), | |
abe05a73 | 294 | }, |
dfeec247 | 295 | HirNode::ImplItem(item) => match item.kind { |
ba9703b0 | 296 | ImplItemKind::Fn(..) => ("Node::ImplItem", LABELS_FN_IN_IMPL), |
dfeec247 | 297 | ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL), |
2b03887a | 298 | ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL), |
abe05a73 | 299 | }, |
9ffffee4 FG |
300 | _ => self.tcx.sess.emit_fatal(errors::UndefinedCleanDirty { |
301 | span: attr.span, | |
302 | kind: format!("{:?}", node), | |
303 | }), | |
abe05a73 | 304 | }; |
74b04a01 XL |
305 | let labels = |
306 | Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string()))); | |
abe05a73 | 307 | (name, labels) |
ea8adc8c XL |
308 | } |
309 | ||
3dfed10e | 310 | fn resolve_labels(&self, item: &NestedMetaItem, value: Symbol) -> Labels { |
b7449926 | 311 | let mut out = Labels::default(); |
3dfed10e | 312 | for label in value.as_str().split(',') { |
ea8adc8c XL |
313 | let label = label.trim(); |
314 | if DepNode::has_label_string(label) { | |
315 | if out.contains(label) { | |
9ffffee4 FG |
316 | self.tcx |
317 | .sess | |
318 | .emit_fatal(errors::RepeatedDepNodeLabel { span: item.span(), label }); | |
54a0048b | 319 | } |
ea8adc8c XL |
320 | out.insert(label.to_string()); |
321 | } else { | |
dfeec247 XL |
322 | self.tcx |
323 | .sess | |
9ffffee4 | 324 | .emit_fatal(errors::UnrecognizedDepNodeLabel { span: item.span(), label }); |
54a0048b SL |
325 | } |
326 | } | |
ea8adc8c XL |
327 | out |
328 | } | |
54a0048b | 329 | |
041b39d2 XL |
330 | fn dep_node_str(&self, dep_node: &DepNode) -> String { |
331 | if let Some(def_id) = dep_node.extract_def_id(self.tcx) { | |
dfeec247 | 332 | format!("{:?}({})", dep_node.kind, self.tcx.def_path_str(def_id)) |
041b39d2 XL |
333 | } else { |
334 | format!("{:?}({:?})", dep_node.kind, dep_node.hash) | |
335 | } | |
54a0048b SL |
336 | } |
337 | ||
041b39d2 | 338 | fn assert_dirty(&self, item_span: Span, dep_node: DepNode) { |
54a0048b SL |
339 | debug!("assert_dirty({:?})", dep_node); |
340 | ||
cdc7bbd5 | 341 | if self.tcx.dep_graph.is_green(&dep_node) { |
ea8adc8c | 342 | let dep_node_str = self.dep_node_str(&dep_node); |
dfeec247 XL |
343 | self.tcx |
344 | .sess | |
9ffffee4 | 345 | .emit_err(errors::NotDirty { span: item_span, dep_node_str: &dep_node_str }); |
54a0048b SL |
346 | } |
347 | } | |
348 | ||
041b39d2 | 349 | fn assert_clean(&self, item_span: Span, dep_node: DepNode) { |
54a0048b SL |
350 | debug!("assert_clean({:?})", dep_node); |
351 | ||
cdc7bbd5 | 352 | if self.tcx.dep_graph.is_red(&dep_node) { |
ea8adc8c | 353 | let dep_node_str = self.dep_node_str(&dep_node); |
dfeec247 XL |
354 | self.tcx |
355 | .sess | |
9ffffee4 | 356 | .emit_err(errors::NotClean { span: item_span, dep_node_str: &dep_node_str }); |
54a0048b SL |
357 | } |
358 | } | |
54a0048b | 359 | |
a2a8927a XL |
360 | fn assert_loaded_from_disk(&self, item_span: Span, dep_node: DepNode) { |
361 | debug!("assert_loaded_from_disk({:?})", dep_node); | |
362 | ||
363 | if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) { | |
364 | let dep_node_str = self.dep_node_str(&dep_node); | |
9ffffee4 FG |
365 | self.tcx |
366 | .sess | |
367 | .emit_err(errors::NotLoaded { span: item_span, dep_node_str: &dep_node_str }); | |
a2a8927a XL |
368 | } |
369 | } | |
370 | ||
04454e1e FG |
371 | fn check_item(&mut self, item_id: LocalDefId) { |
372 | let item_span = self.tcx.def_span(item_id.to_def_id()); | |
3c0e092e | 373 | let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id()); |
353b0b11 | 374 | for attr in self.tcx.get_attrs(item_id, sym::rustc_clean) { |
5e7ed085 FG |
375 | let Some(assertion) = self.assertion_maybe(item_id, attr) else { |
376 | continue; | |
abe05a73 XL |
377 | }; |
378 | self.checked_attrs.insert(attr.id); | |
3c0e092e XL |
379 | for label in assertion.clean { |
380 | let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); | |
abe05a73 XL |
381 | self.assert_clean(item_span, dep_node); |
382 | } | |
3c0e092e XL |
383 | for label in assertion.dirty { |
384 | let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); | |
abe05a73 | 385 | self.assert_dirty(item_span, dep_node); |
54a0048b | 386 | } |
a2a8927a XL |
387 | for label in assertion.loaded_from_disk { |
388 | let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); | |
389 | self.assert_loaded_from_disk(item_span, dep_node); | |
390 | } | |
54a0048b SL |
391 | } |
392 | } | |
8bb4bdeb XL |
393 | } |
394 | ||
17df50a5 XL |
395 | /// Given a `#[rustc_clean]` attribute, scan for a `cfg="foo"` attribute and check whether we have |
396 | /// a cfg flag called `foo`. | |
dc9dc135 | 397 | fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool { |
9e0c209e | 398 | debug!("check_config(attr={:?})", attr); |
c30ab7b3 | 399 | let config = &tcx.sess.parse_sess.config; |
9e0c209e | 400 | debug!("check_config: config={:?}", config); |
17df50a5 | 401 | let mut cfg = None; |
9ffffee4 | 402 | for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { |
3dfed10e | 403 | if item.has_name(CFG) { |
cc61c64b | 404 | let value = expect_associated_value(tcx, &item); |
9e0c209e | 405 | debug!("check_config: searching for cfg {:?}", value); |
abe05a73 | 406 | cfg = Some(config.contains(&(value, None))); |
a2a8927a | 407 | } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) { |
9ffffee4 | 408 | tcx.sess.emit_err(errors::UnknownItem { span: attr.span, name: item.name_or_empty() }); |
9e0c209e SL |
409 | } |
410 | } | |
411 | ||
abe05a73 | 412 | match cfg { |
9ffffee4 | 413 | None => tcx.sess.emit_fatal(errors::NoCfg { span: attr.span }), |
abe05a73 XL |
414 | Some(c) => c, |
415 | } | |
9e0c209e SL |
416 | } |
417 | ||
f9f354fc | 418 | fn expect_associated_value(tcx: TyCtxt<'_>, item: &NestedMetaItem) -> Symbol { |
9e0c209e SL |
419 | if let Some(value) = item.value_str() { |
420 | value | |
421 | } else { | |
9ffffee4 FG |
422 | if let Some(ident) = item.ident() { |
423 | tcx.sess.emit_fatal(errors::AssociatedValueExpectedFor { span: item.span(), ident }); | |
9e0c209e | 424 | } else { |
9ffffee4 FG |
425 | tcx.sess.emit_fatal(errors::AssociatedValueExpected { span: item.span() }); |
426 | } | |
9e0c209e SL |
427 | } |
428 | } | |
8bb4bdeb | 429 | |
487cf647 FG |
430 | /// A visitor that collects all `#[rustc_clean]` attributes from |
431 | /// the HIR. It is used to verify that we really ran checks for all annotated | |
432 | /// nodes. | |
17df50a5 | 433 | pub struct FindAllAttrs<'tcx> { |
dc9dc135 | 434 | tcx: TyCtxt<'tcx>, |
8bb4bdeb XL |
435 | found_attrs: Vec<&'tcx Attribute>, |
436 | } | |
437 | ||
a2a8927a | 438 | impl<'tcx> FindAllAttrs<'tcx> { |
8bb4bdeb | 439 | fn is_active_attr(&mut self, attr: &Attribute) -> bool { |
94222f64 | 440 | if attr.has_name(sym::rustc_clean) && check_config(self.tcx, attr) { |
17df50a5 | 441 | return true; |
8bb4bdeb XL |
442 | } |
443 | ||
444 | false | |
445 | } | |
446 | ||
6a06907d | 447 | fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<ast::AttrId>) { |
8bb4bdeb XL |
448 | for attr in &self.found_attrs { |
449 | if !checked_attrs.contains(&attr.id) { | |
9ffffee4 | 450 | self.tcx.sess.emit_err(errors::UncheckedClean { span: attr.span }); |
6a06907d | 451 | checked_attrs.insert(attr.id); |
8bb4bdeb XL |
452 | } |
453 | } | |
454 | } | |
455 | } | |
456 | ||
a2a8927a | 457 | impl<'tcx> intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> { |
5099ac24 | 458 | type NestedFilter = nested_filter::All; |
dfeec247 | 459 | |
5099ac24 FG |
460 | fn nested_visit_map(&mut self) -> Self::Map { |
461 | self.tcx.hir() | |
8bb4bdeb XL |
462 | } |
463 | ||
923072b8 | 464 | fn visit_attribute(&mut self, attr: &'tcx Attribute) { |
8bb4bdeb XL |
465 | if self.is_active_attr(attr) { |
466 | self.found_attrs.push(attr); | |
467 | } | |
468 | } | |
469 | } |