rustc_incremental/persist/
clean.rs1use rustc_attr_ir::{Attribute, AttributeKind, RustcCleanAttribute, find_attr};
23use rustc_data_structures::fx::FxHashSet;
24use rustc_data_structures::unord::UnordSet;
25use rustc_hir::def_id::LocalDefId;
26use rustc_hir::{ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit};
27use rustc_middle::dep_graph::{DepKind, DepNode, dep_kind_from_label};
28use rustc_middle::hir::nested_filter;
29use rustc_middle::ty::TyCtxt;
30use rustc_span::{Span, Symbol};
31use tracing::debug;
32
33use crate::diagnostics;
34
35const BASE_CONST: &[DepKind] = &[DepKind::type_of];
39
40const BASE_FN: &[DepKind] = &[
42 DepKind::fn_sig,
44 DepKind::generics_of,
45 DepKind::clauses_of,
46 DepKind::type_of,
47 DepKind::typeck_root,
50];
51
52const BASE_HIR: &[DepKind] = &[
54 DepKind::hir_owner,
56];
57
58const BASE_IMPL: &[DepKind] =
60 &[DepKind::associated_item_def_ids, DepKind::generics_of, DepKind::impl_trait_header];
61
62const BASE_MIR: &[DepKind] = &[DepKind::optimized_mir, DepKind::promoted_mir];
65
66const BASE_STRUCT: &[DepKind] = &[DepKind::generics_of, DepKind::clauses_of, DepKind::type_of];
71
72const EXTRA_ASSOCIATED: &[DepKind] = &[DepKind::associated_item];
75
76const EXTRA_TRAIT: &[DepKind] = &[];
77
78const LABELS_CONST: &[&[DepKind]] = &[BASE_HIR, BASE_CONST];
81
82const LABELS_CONST_IN_IMPL: &[&[DepKind]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED];
84
85const LABELS_CONST_IN_TRAIT: &[&[DepKind]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED, EXTRA_TRAIT];
87
88const LABELS_FN: &[&[DepKind]] = &[BASE_HIR, BASE_MIR, BASE_FN];
90
91const LABELS_FN_IN_IMPL: &[&[DepKind]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED];
93
94const LABELS_FN_IN_TRAIT: &[&[DepKind]] =
96 &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED, EXTRA_TRAIT];
97
98const LABELS_HIR_ONLY: &[&[DepKind]] = &[BASE_HIR];
100
101const LABELS_TRAIT: &[&[DepKind]] =
103 &[BASE_HIR, &[DepKind::associated_item_def_ids, DepKind::clauses_of, DepKind::generics_of]];
104
105const LABELS_IMPL: &[&[DepKind]] = &[BASE_HIR, BASE_IMPL];
107
108const LABELS_ADT: &[&[DepKind]] = &[BASE_HIR, BASE_STRUCT];
110
111type Labels = UnordSet<String>;
119
120struct Assertion {
122 clean: Labels,
123 dirty: Labels,
124 loaded_from_disk: Labels,
125}
126
127pub(crate) fn check_clean_annotations(tcx: TyCtxt<'_>) {
128 if !tcx.sess.opts.unstable_opts.query_dep_graph {
129 return;
130 }
131
132 if !tcx.features().rustc_attrs() {
134 return;
135 }
136
137 tcx.dep_graph.with_ignore(|| {
138 let mut clean_visitor = CleanVisitor { tcx, checked_attrs: Default::default() };
139
140 let crate_items = tcx.hir_crate_items(());
141
142 for id in crate_items.free_items() {
143 clean_visitor.check_item(id.owner_id.def_id);
144 }
145
146 for id in crate_items.trait_items() {
147 clean_visitor.check_item(id.owner_id.def_id);
148 }
149
150 for id in crate_items.impl_items() {
151 clean_visitor.check_item(id.owner_id.def_id);
152 }
153
154 for id in crate_items.foreign_items() {
155 clean_visitor.check_item(id.owner_id.def_id);
156 }
157
158 let mut all_attrs = FindAllAttrs { tcx, found_attrs: ::alloc::vec::Vec::new()vec![] };
159 tcx.hir_walk_attributes(&mut all_attrs);
160
161 all_attrs.report_unchecked_attrs(clean_visitor.checked_attrs);
165 })
166}
167
168struct CleanVisitor<'tcx> {
169 tcx: TyCtxt<'tcx>,
170 checked_attrs: FxHashSet<Span>,
171}
172
173impl<'tcx> CleanVisitor<'tcx> {
174 fn assertion_maybe(
176 &mut self,
177 item_id: LocalDefId,
178 attr: &RustcCleanAttribute,
179 ) -> Option<Assertion> {
180 self.tcx.sess.config.contains(&(attr.cfg, None)).then(|| self.assertion_auto(item_id, attr))
181 }
182
183 fn assertion_auto(&mut self, item_id: LocalDefId, attr: &RustcCleanAttribute) -> Assertion {
185 let (name, mut auto) = self.auto_labels(item_id, attr.span);
186 let except = self.except(attr);
187 let loaded_from_disk = self.loaded_from_disk(attr);
188 for e in except.items().into_sorted_stable_ord() {
189 if !auto.remove(e) {
190 self.tcx.dcx().emit_fatal(diagnostics::AssertionAuto { span: attr.span, name, e });
191 }
192 }
193 Assertion { clean: auto, dirty: except, loaded_from_disk }
194 }
195
196 fn loaded_from_disk(&self, attr: &RustcCleanAttribute) -> Labels {
198 attr.loaded_from_disk
199 .as_ref()
200 .map(|queries| self.resolve_labels(&queries.entries, queries.span))
201 .unwrap_or_default()
202 }
203
204 fn except(&self, attr: &RustcCleanAttribute) -> Labels {
206 attr.except
207 .as_ref()
208 .map(|queries| self.resolve_labels(&queries.entries, queries.span))
209 .unwrap_or_default()
210 }
211
212 fn auto_labels(&mut self, item_id: LocalDefId, span: Span) -> (&'static str, Labels) {
215 let node = self.tcx.hir_node_by_def_id(item_id);
216 let (name, labels) = match node {
217 HirNode::Item(item) => {
218 match item.kind {
219 HirItem::Static(..) => ("ItemStatic", LABELS_CONST),
230
231 HirItem::Const(..) => ("ItemConst", LABELS_CONST),
233
234 HirItem::Fn { .. } => ("ItemFn", LABELS_FN),
236
237 HirItem::Mod(..) => ("ItemMod", LABELS_HIR_ONLY),
239
240 HirItem::ForeignMod { .. } => ("ItemForeignMod", LABELS_HIR_ONLY),
242
243 HirItem::GlobalAsm { .. } => ("ItemGlobalAsm", LABELS_HIR_ONLY),
245
246 HirItem::TyAlias(..) => ("ItemTy", LABELS_HIR_ONLY),
248
249 HirItem::Enum(..) => ("ItemEnum", LABELS_ADT),
251
252 HirItem::Struct(..) => ("ItemStruct", LABELS_ADT),
254
255 HirItem::Union(..) => ("ItemUnion", LABELS_ADT),
257
258 HirItem::Trait { .. } => ("ItemTrait", LABELS_TRAIT),
260
261 HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
263
264 _ => self.tcx.dcx().emit_fatal(diagnostics::UndefinedCleanDirtyItem {
265 span,
266 kind: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", item.kind))
})format!("{:?}", item.kind),
267 }),
268 }
269 }
270 HirNode::TraitItem(item) => match item.kind {
271 TraitItemKind::Fn(..) => ("Node::TraitItem", LABELS_FN_IN_TRAIT),
272 TraitItemKind::Const(..) => ("NodeTraitConst", LABELS_CONST_IN_TRAIT),
273 TraitItemKind::Type(..) => ("NodeTraitType", LABELS_CONST_IN_TRAIT),
274 },
275 HirNode::ImplItem(item) => match item.kind {
276 ImplItemKind::Fn(..) => ("Node::ImplItem", LABELS_FN_IN_IMPL),
277 ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL),
278 ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
279 },
280 _ => self
281 .tcx
282 .dcx()
283 .emit_fatal(diagnostics::UndefinedCleanDirty { span, kind: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", node))
})format!("{node:?}") }),
284 };
285 let labels =
286 Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", l))
})format!("{l:?}"))));
287 (name, labels)
288 }
289
290 fn resolve_labels(&self, values: &[Symbol], span: Span) -> Labels {
291 let mut out = Labels::default();
292 for label in values {
293 let label_str = label.as_str();
294 if DepNode::has_label_string(label_str) {
295 if out.contains(label_str) {
296 self.tcx
297 .dcx()
298 .emit_fatal(diagnostics::RepeatedDepNodeLabel { span, label: label_str });
299 }
300 out.insert(label_str.to_string());
301 } else {
302 self.tcx
303 .dcx()
304 .emit_fatal(diagnostics::UnrecognizedDepNodeLabel { span, label: label_str });
305 }
306 }
307 out
308 }
309
310 fn dep_node_str(&self, dep_node: &DepNode) -> String {
311 if let Some(def_id) = dep_node.extract_def_id(self.tcx) {
312 ::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))
313 } else {
314 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}({1:?})", dep_node.kind,
dep_node.key_fingerprint))
})format!("{:?}({:?})", dep_node.kind, dep_node.key_fingerprint)
315 }
316 }
317
318 fn assert_dirty(&self, item_span: Span, dep_node: DepNode) {
319 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_incremental/src/persist/clean.rs:319",
"rustc_incremental::persist::clean",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_incremental/src/persist/clean.rs"),
::tracing_core::__macro_support::Option::Some(319u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("assert_dirty({0:?})",
dep_node) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("assert_dirty({:?})", dep_node);
320
321 if self.tcx.dep_graph.is_green(&dep_node) {
322 let dep_node_str = self.dep_node_str(&dep_node);
323 self.tcx
324 .dcx()
325 .emit_err(diagnostics::NotDirty { span: item_span, dep_node_str: &dep_node_str });
326 }
327 }
328
329 fn assert_clean(&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 /rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_incremental/src/persist/clean.rs:330",
"rustc_incremental::persist::clean",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("assert_clean({0:?})",
dep_node) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("assert_clean({:?})", dep_node);
331
332 if self.tcx.dep_graph.is_red(&dep_node) {
333 let dep_node_str = self.dep_node_str(&dep_node);
334 self.tcx
335 .dcx()
336 .emit_err(diagnostics::NotClean { span: item_span, dep_node_str: &dep_node_str });
337 }
338 }
339
340 fn check_item(&mut self, item_id: LocalDefId) {
341 let item_span = self.tcx.def_span(item_id.to_def_id());
342 let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id());
343
344 let Some(clean_attrs) = {
{
'done:
{
for i in ::rustc_attr_ir::HasAttrs::get_attrs(item_id, &self.tcx)
{
#[allow(unused_imports)]
use ::rustc_attr_ir::AttributeKind::*;
let i: &::rustc_attr_ir::Attribute = i;
match i {
::rustc_attr_ir::Attribute::Parsed(RustcClean(attr)) => {
break 'done Some(attr);
}
::rustc_attr_ir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}find_attr!(self.tcx, item_id, RustcClean(attr) => attr) else {
345 return;
346 };
347
348 for attr in clean_attrs {
349 let Some(assertion) = self.assertion_maybe(item_id, attr) else {
350 continue;
351 };
352 self.checked_attrs.insert(attr.span);
353 for label in assertion.clean.items().into_sorted_stable_ord() {
354 let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
355 self.assert_clean(item_span, dep_node);
356 }
357 for label in assertion.dirty.items().into_sorted_stable_ord() {
358 let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
359 self.assert_dirty(item_span, dep_node);
360 }
361 for label in assertion.loaded_from_disk.items().into_sorted_stable_ord() {
362 match DepNode::from_label_string(self.tcx, label, def_path_hash) {
363 Ok(dep_node) => {
364 if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) {
365 let dep_node_str = self.dep_node_str(&dep_node);
366 self.tcx.dcx().emit_err(diagnostics::NotLoaded {
367 span: item_span,
368 dep_node_str: &dep_node_str,
369 });
370 }
371 }
372 Err(()) => {
374 let dep_kind = dep_kind_from_label(label);
375 if !self.tcx.dep_graph.debug_dep_kind_was_loaded_from_disk(dep_kind) {
376 self.tcx.dcx().emit_err(diagnostics::NotLoaded {
377 span: item_span,
378 dep_node_str: &label,
379 });
380 }
381 }
382 }
383 }
384 }
385 }
386}
387
388struct FindAllAttrs<'tcx> {
392 tcx: TyCtxt<'tcx>,
393 found_attrs: Vec<&'tcx RustcCleanAttribute>,
394}
395
396impl<'tcx> FindAllAttrs<'tcx> {
397 fn is_active_attr(&self, attr: &RustcCleanAttribute) -> bool {
398 self.tcx.sess.config.contains(&(attr.cfg, None))
399 }
400
401 fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<Span>) {
402 for attr in &self.found_attrs {
403 if !checked_attrs.contains(&attr.span) {
404 self.tcx.dcx().emit_err(diagnostics::UncheckedClean { span: attr.span });
405 checked_attrs.insert(attr.span);
406 }
407 }
408 }
409}
410
411impl<'tcx> intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> {
412 type NestedFilter = nested_filter::All;
413
414 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
415 self.tcx
416 }
417
418 fn visit_attribute(&mut self, attr: &'tcx Attribute) {
419 if let Attribute::Parsed(AttributeKind::RustcClean(attrs)) = attr {
420 for attr in attrs {
421 if self.is_active_attr(attr) {
422 self.found_attrs.push(attr);
423 }
424 }
425 }
426 }
427}