rustc_incremental/persist/
clean.rs1use rustc_data_structures::fx::FxHashSet;
23use rustc_data_structures::unord::UnordSet;
24use rustc_hir::attrs::{AttributeKind, RustcCleanAttribute};
25use rustc_hir::def_id::LocalDefId;
26use rustc_hir::{
27 Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, find_attr,
28 intravisit,
29};
30use rustc_middle::dep_graph::{DepNode, DepNodeExt, dep_kind_from_label, label_strs};
31use rustc_middle::hir::nested_filter;
32use rustc_middle::ty::TyCtxt;
33use rustc_span::{Span, Symbol};
34use tracing::debug;
35
36use crate::errors;
37
38const BASE_CONST: &[&str] = &[label_strs::type_of];
42
43const BASE_FN: &[&str] = &[
45 label_strs::fn_sig,
47 label_strs::generics_of,
48 label_strs::predicates_of,
49 label_strs::type_of,
50 label_strs::typeck,
53];
54
55const BASE_HIR: &[&str] = &[
57 label_strs::opt_hir_owner_nodes,
59];
60
61const BASE_IMPL: &[&str] =
63 &[label_strs::associated_item_def_ids, label_strs::generics_of, label_strs::impl_trait_header];
64
65const BASE_MIR: &[&str] = &[label_strs::optimized_mir, label_strs::promoted_mir];
68
69const BASE_STRUCT: &[&str] =
74 &[label_strs::generics_of, label_strs::predicates_of, label_strs::type_of];
75
76const EXTRA_ASSOCIATED: &[&str] = &[label_strs::associated_item];
79
80const EXTRA_TRAIT: &[&str] = &[];
81
82const LABELS_CONST: &[&[&str]] = &[BASE_HIR, BASE_CONST];
85
86const LABELS_CONST_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED];
88
89const LABELS_CONST_IN_TRAIT: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED, EXTRA_TRAIT];
91
92const LABELS_FN: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN];
94
95const LABELS_FN_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED];
97
98const LABELS_FN_IN_TRAIT: &[&[&str]] =
100 &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED, EXTRA_TRAIT];
101
102const LABELS_HIR_ONLY: &[&[&str]] = &[BASE_HIR];
104
105const LABELS_TRAIT: &[&[&str]] = &[
107 BASE_HIR,
108 &[label_strs::associated_item_def_ids, label_strs::predicates_of, label_strs::generics_of],
109];
110
111const LABELS_IMPL: &[&[&str]] = &[BASE_HIR, BASE_IMPL];
113
114const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT];
116
117type Labels = UnordSet<String>;
125
126struct Assertion {
128 clean: Labels,
129 dirty: Labels,
130 loaded_from_disk: Labels,
131}
132
133pub(crate) fn check_clean_annotations(tcx: TyCtxt<'_>) {
134 if !tcx.sess.opts.unstable_opts.query_dep_graph {
135 return;
136 }
137
138 if !tcx.features().rustc_attrs() {
140 return;
141 }
142
143 tcx.dep_graph.with_ignore(|| {
144 let mut clean_visitor = CleanVisitor { tcx, checked_attrs: Default::default() };
145
146 let crate_items = tcx.hir_crate_items(());
147
148 for id in crate_items.free_items() {
149 clean_visitor.check_item(id.owner_id.def_id);
150 }
151
152 for id in crate_items.trait_items() {
153 clean_visitor.check_item(id.owner_id.def_id);
154 }
155
156 for id in crate_items.impl_items() {
157 clean_visitor.check_item(id.owner_id.def_id);
158 }
159
160 for id in crate_items.foreign_items() {
161 clean_visitor.check_item(id.owner_id.def_id);
162 }
163
164 let mut all_attrs = FindAllAttrs { tcx, found_attrs: ::alloc::vec::Vec::new()vec![] };
165 tcx.hir_walk_attributes(&mut all_attrs);
166
167 all_attrs.report_unchecked_attrs(clean_visitor.checked_attrs);
171 })
172}
173
174struct CleanVisitor<'tcx> {
175 tcx: TyCtxt<'tcx>,
176 checked_attrs: FxHashSet<Span>,
177}
178
179impl<'tcx> CleanVisitor<'tcx> {
180 fn assertion_maybe(
182 &mut self,
183 item_id: LocalDefId,
184 attr: &RustcCleanAttribute,
185 ) -> Option<Assertion> {
186 self.tcx
187 .sess
188 .psess
189 .config
190 .contains(&(attr.cfg, None))
191 .then(|| self.assertion_auto(item_id, attr))
192 }
193
194 fn assertion_auto(&mut self, item_id: LocalDefId, attr: &RustcCleanAttribute) -> Assertion {
196 let (name, mut auto) = self.auto_labels(item_id, attr.span);
197 let except = self.except(attr);
198 let loaded_from_disk = self.loaded_from_disk(attr);
199 for e in except.items().into_sorted_stable_ord() {
200 if !auto.remove(e) {
201 self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span, name, e });
202 }
203 }
204 Assertion { clean: auto, dirty: except, loaded_from_disk }
205 }
206
207 fn loaded_from_disk(&self, attr: &RustcCleanAttribute) -> Labels {
209 attr.loaded_from_disk
210 .as_ref()
211 .map(|queries| self.resolve_labels(&queries.entries, queries.span))
212 .unwrap_or_default()
213 }
214
215 fn except(&self, attr: &RustcCleanAttribute) -> Labels {
217 attr.except
218 .as_ref()
219 .map(|queries| self.resolve_labels(&queries.entries, queries.span))
220 .unwrap_or_default()
221 }
222
223 fn auto_labels(&mut self, item_id: LocalDefId, span: Span) -> (&'static str, Labels) {
226 let node = self.tcx.hir_node_by_def_id(item_id);
227 let (name, labels) = match node {
228 HirNode::Item(item) => {
229 match item.kind {
230 HirItem::Static(..) => ("ItemStatic", LABELS_CONST),
241
242 HirItem::Const(..) => ("ItemConst", LABELS_CONST),
244
245 HirItem::Fn { .. } => ("ItemFn", LABELS_FN),
247
248 HirItem::Mod(..) => ("ItemMod", LABELS_HIR_ONLY),
250
251 HirItem::ForeignMod { .. } => ("ItemForeignMod", LABELS_HIR_ONLY),
253
254 HirItem::GlobalAsm { .. } => ("ItemGlobalAsm", LABELS_HIR_ONLY),
256
257 HirItem::TyAlias(..) => ("ItemTy", LABELS_HIR_ONLY),
259
260 HirItem::Enum(..) => ("ItemEnum", LABELS_ADT),
262
263 HirItem::Struct(..) => ("ItemStruct", LABELS_ADT),
265
266 HirItem::Union(..) => ("ItemUnion", LABELS_ADT),
268
269 HirItem::Trait(..) => ("ItemTrait", LABELS_TRAIT),
271
272 HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
274
275 _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem {
276 span,
277 kind: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", item.kind))
})format!("{:?}", item.kind),
278 }),
279 }
280 }
281 HirNode::TraitItem(item) => match item.kind {
282 TraitItemKind::Fn(..) => ("Node::TraitItem", LABELS_FN_IN_TRAIT),
283 TraitItemKind::Const(..) => ("NodeTraitConst", LABELS_CONST_IN_TRAIT),
284 TraitItemKind::Type(..) => ("NodeTraitType", LABELS_CONST_IN_TRAIT),
285 },
286 HirNode::ImplItem(item) => match item.kind {
287 ImplItemKind::Fn(..) => ("Node::ImplItem", LABELS_FN_IN_IMPL),
288 ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL),
289 ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
290 },
291 _ => self
292 .tcx
293 .dcx()
294 .emit_fatal(errors::UndefinedCleanDirty { span, kind: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", node))
})format!("{node:?}") }),
295 };
296 let labels =
297 Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string())));
298 (name, labels)
299 }
300
301 fn resolve_labels(&self, values: &[Symbol], span: Span) -> Labels {
302 let mut out = Labels::default();
303 for label in values {
304 let label_str = label.as_str();
305 if DepNode::has_label_string(label_str) {
306 if out.contains(label_str) {
307 self.tcx
308 .dcx()
309 .emit_fatal(errors::RepeatedDepNodeLabel { span, label: label_str });
310 }
311 out.insert(label_str.to_string());
312 } else {
313 self.tcx
314 .dcx()
315 .emit_fatal(errors::UnrecognizedDepNodeLabel { span, label: label_str });
316 }
317 }
318 out
319 }
320
321 fn dep_node_str(&self, dep_node: &DepNode) -> String {
322 if let Some(def_id) = dep_node.extract_def_id(self.tcx) {
323 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}({1})", dep_node.kind,
self.tcx.def_path_str(def_id)))
})format!("{:?}({})", dep_node.kind, self.tcx.def_path_str(def_id))
324 } else {
325 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}({1:?})", dep_node.kind,
dep_node.hash))
})format!("{:?}({:?})", dep_node.kind, dep_node.hash)
326 }
327 }
328
329 fn assert_dirty(&self, item_span: Span, dep_node: DepNode) {
330 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/clean.rs:330",
"rustc_incremental::persist::clean",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/clean.rs"),
::tracing_core::__macro_support::Option::Some(330u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::clean"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("assert_dirty({0:?})",
dep_node) as &dyn Value))])
});
} else { ; }
};debug!("assert_dirty({:?})", dep_node);
331
332 if self.tcx.dep_graph.is_green(&dep_node) {
333 let dep_node_str = self.dep_node_str(&dep_node);
334 self.tcx
335 .dcx()
336 .emit_err(errors::NotDirty { span: item_span, dep_node_str: &dep_node_str });
337 }
338 }
339
340 fn assert_clean(&self, item_span: Span, dep_node: DepNode) {
341 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_incremental/src/persist/clean.rs:341",
"rustc_incremental::persist::clean",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_incremental/src/persist/clean.rs"),
::tracing_core::__macro_support::Option::Some(341u32),
::tracing_core::__macro_support::Option::Some("rustc_incremental::persist::clean"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("assert_clean({0:?})",
dep_node) as &dyn Value))])
});
} else { ; }
};debug!("assert_clean({:?})", dep_node);
342
343 if self.tcx.dep_graph.is_red(&dep_node) {
344 let dep_node_str = self.dep_node_str(&dep_node);
345 self.tcx
346 .dcx()
347 .emit_err(errors::NotClean { span: item_span, dep_node_str: &dep_node_str });
348 }
349 }
350
351 fn check_item(&mut self, item_id: LocalDefId) {
352 let item_span = self.tcx.def_span(item_id.to_def_id());
353 let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id());
354
355 let Some(attr) =
356 {
'done:
{
for i in self.tcx.get_all_attrs(item_id) {
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(AttributeKind::RustcClean(attr))
=> {
break 'done Some(attr);
}
_ => {}
}
}
None
}
}find_attr!(self.tcx.get_all_attrs(item_id), AttributeKind::RustcClean(attr) => attr)
357 else {
358 return;
359 };
360
361 for attr in attr {
362 let Some(assertion) = self.assertion_maybe(item_id, attr) else {
363 continue;
364 };
365 self.checked_attrs.insert(attr.span);
366 for label in assertion.clean.items().into_sorted_stable_ord() {
367 let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
368 self.assert_clean(item_span, dep_node);
369 }
370 for label in assertion.dirty.items().into_sorted_stable_ord() {
371 let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
372 self.assert_dirty(item_span, dep_node);
373 }
374 for label in assertion.loaded_from_disk.items().into_sorted_stable_ord() {
375 match DepNode::from_label_string(self.tcx, label, def_path_hash) {
376 Ok(dep_node) => {
377 if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) {
378 let dep_node_str = self.dep_node_str(&dep_node);
379 self.tcx.dcx().emit_err(errors::NotLoaded {
380 span: item_span,
381 dep_node_str: &dep_node_str,
382 });
383 }
384 }
385 Err(()) => {
387 let dep_kind = dep_kind_from_label(label);
388 if !self.tcx.dep_graph.debug_dep_kind_was_loaded_from_disk(dep_kind) {
389 self.tcx.dcx().emit_err(errors::NotLoaded {
390 span: item_span,
391 dep_node_str: &label,
392 });
393 }
394 }
395 }
396 }
397 }
398 }
399}
400
401struct FindAllAttrs<'tcx> {
405 tcx: TyCtxt<'tcx>,
406 found_attrs: Vec<&'tcx RustcCleanAttribute>,
407}
408
409impl<'tcx> FindAllAttrs<'tcx> {
410 fn is_active_attr(&self, attr: &RustcCleanAttribute) -> bool {
411 self.tcx.sess.psess.config.contains(&(attr.cfg, None))
412 }
413
414 fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<Span>) {
415 for attr in &self.found_attrs {
416 if !checked_attrs.contains(&attr.span) {
417 self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span });
418 checked_attrs.insert(attr.span);
419 }
420 }
421 }
422}
423
424impl<'tcx> intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> {
425 type NestedFilter = nested_filter::All;
426
427 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
428 self.tcx
429 }
430
431 fn visit_attribute(&mut self, attr: &'tcx Attribute) {
432 if let Attribute::Parsed(AttributeKind::RustcClean(attrs)) = attr {
433 for attr in attrs {
434 if self.is_active_attr(attr) {
435 self.found_attrs.push(attr);
436 }
437 }
438 }
439 }
440}