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::{DepKind, DepNode, dep_kind_from_label};
31use rustc_middle::hir::nested_filter;
32use rustc_middle::ty::TyCtxt;
33use rustc_span::{Span, Symbol};
34use tracing::debug;
35
36use crate::diagnostics;
37
38const BASE_CONST: &[DepKind] = &[DepKind::type_of];
42
43const BASE_FN: &[DepKind] = &[
45 DepKind::fn_sig,
47 DepKind::generics_of,
48 DepKind::clauses_of,
49 DepKind::type_of,
50 DepKind::typeck_root,
53];
54
55const BASE_HIR: &[DepKind] = &[
57 DepKind::hir_owner,
59];
60
61const BASE_IMPL: &[DepKind] =
63 &[DepKind::associated_item_def_ids, DepKind::generics_of, DepKind::impl_trait_header];
64
65const BASE_MIR: &[DepKind] = &[DepKind::optimized_mir, DepKind::promoted_mir];
68
69const BASE_STRUCT: &[DepKind] = &[DepKind::generics_of, DepKind::clauses_of, DepKind::type_of];
74
75const EXTRA_ASSOCIATED: &[DepKind] = &[DepKind::associated_item];
78
79const EXTRA_TRAIT: &[DepKind] = &[];
80
81const LABELS_CONST: &[&[DepKind]] = &[BASE_HIR, BASE_CONST];
84
85const LABELS_CONST_IN_IMPL: &[&[DepKind]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED];
87
88const LABELS_CONST_IN_TRAIT: &[&[DepKind]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED, EXTRA_TRAIT];
90
91const LABELS_FN: &[&[DepKind]] = &[BASE_HIR, BASE_MIR, BASE_FN];
93
94const LABELS_FN_IN_IMPL: &[&[DepKind]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED];
96
97const LABELS_FN_IN_TRAIT: &[&[DepKind]] =
99 &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED, EXTRA_TRAIT];
100
101const LABELS_HIR_ONLY: &[&[DepKind]] = &[BASE_HIR];
103
104const LABELS_TRAIT: &[&[DepKind]] =
106 &[BASE_HIR, &[DepKind::associated_item_def_ids, DepKind::clauses_of, DepKind::generics_of]];
107
108const LABELS_IMPL: &[&[DepKind]] = &[BASE_HIR, BASE_IMPL];
110
111const LABELS_ADT: &[&[DepKind]] = &[BASE_HIR, BASE_STRUCT];
113
114type Labels = UnordSet<String>;
122
123struct Assertion {
125 clean: Labels,
126 dirty: Labels,
127 loaded_from_disk: Labels,
128}
129
130pub(crate) fn check_clean_annotations(tcx: TyCtxt<'_>) {
131 if !tcx.sess.opts.unstable_opts.query_dep_graph {
132 return;
133 }
134
135 if !tcx.features().rustc_attrs() {
137 return;
138 }
139
140 tcx.dep_graph.with_ignore(|| {
141 let mut clean_visitor = CleanVisitor { tcx, checked_attrs: Default::default() };
142
143 let crate_items = tcx.hir_crate_items(());
144
145 for id in crate_items.free_items() {
146 clean_visitor.check_item(id.owner_id.def_id);
147 }
148
149 for id in crate_items.trait_items() {
150 clean_visitor.check_item(id.owner_id.def_id);
151 }
152
153 for id in crate_items.impl_items() {
154 clean_visitor.check_item(id.owner_id.def_id);
155 }
156
157 for id in crate_items.foreign_items() {
158 clean_visitor.check_item(id.owner_id.def_id);
159 }
160
161 let mut all_attrs = FindAllAttrs { tcx, found_attrs: ::alloc::vec::Vec::new()vec![] };
162 tcx.hir_walk_attributes(&mut all_attrs);
163
164 all_attrs.report_unchecked_attrs(clean_visitor.checked_attrs);
168 })
169}
170
171struct CleanVisitor<'tcx> {
172 tcx: TyCtxt<'tcx>,
173 checked_attrs: FxHashSet<Span>,
174}
175
176impl<'tcx> CleanVisitor<'tcx> {
177 fn assertion_maybe(
179 &mut self,
180 item_id: LocalDefId,
181 attr: &RustcCleanAttribute,
182 ) -> Option<Assertion> {
183 self.tcx.sess.config.contains(&(attr.cfg, None)).then(|| self.assertion_auto(item_id, attr))
184 }
185
186 fn assertion_auto(&mut self, item_id: LocalDefId, attr: &RustcCleanAttribute) -> Assertion {
188 let (name, mut auto) = self.auto_labels(item_id, attr.span);
189 let except = self.except(attr);
190 let loaded_from_disk = self.loaded_from_disk(attr);
191 for e in except.items().into_sorted_stable_ord() {
192 if !auto.remove(e) {
193 self.tcx.dcx().emit_fatal(diagnostics::AssertionAuto { span: attr.span, name, e });
194 }
195 }
196 Assertion { clean: auto, dirty: except, loaded_from_disk }
197 }
198
199 fn loaded_from_disk(&self, attr: &RustcCleanAttribute) -> Labels {
201 attr.loaded_from_disk
202 .as_ref()
203 .map(|queries| self.resolve_labels(&queries.entries, queries.span))
204 .unwrap_or_default()
205 }
206
207 fn except(&self, attr: &RustcCleanAttribute) -> Labels {
209 attr.except
210 .as_ref()
211 .map(|queries| self.resolve_labels(&queries.entries, queries.span))
212 .unwrap_or_default()
213 }
214
215 fn auto_labels(&mut self, item_id: LocalDefId, span: Span) -> (&'static str, Labels) {
218 let node = self.tcx.hir_node_by_def_id(item_id);
219 let (name, labels) = match node {
220 HirNode::Item(item) => {
221 match item.kind {
222 HirItem::Static(..) => ("ItemStatic", LABELS_CONST),
233
234 HirItem::Const(..) => ("ItemConst", LABELS_CONST),
236
237 HirItem::Fn { .. } => ("ItemFn", LABELS_FN),
239
240 HirItem::Mod(..) => ("ItemMod", LABELS_HIR_ONLY),
242
243 HirItem::ForeignMod { .. } => ("ItemForeignMod", LABELS_HIR_ONLY),
245
246 HirItem::GlobalAsm { .. } => ("ItemGlobalAsm", LABELS_HIR_ONLY),
248
249 HirItem::TyAlias(..) => ("ItemTy", LABELS_HIR_ONLY),
251
252 HirItem::Enum(..) => ("ItemEnum", LABELS_ADT),
254
255 HirItem::Struct(..) => ("ItemStruct", LABELS_ADT),
257
258 HirItem::Union(..) => ("ItemUnion", LABELS_ADT),
260
261 HirItem::Trait { .. } => ("ItemTrait", LABELS_TRAIT),
263
264 HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
266
267 _ => self.tcx.dcx().emit_fatal(diagnostics::UndefinedCleanDirtyItem {
268 span,
269 kind: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", item.kind))
})format!("{:?}", item.kind),
270 }),
271 }
272 }
273 HirNode::TraitItem(item) => match item.kind {
274 TraitItemKind::Fn(..) => ("Node::TraitItem", LABELS_FN_IN_TRAIT),
275 TraitItemKind::Const(..) => ("NodeTraitConst", LABELS_CONST_IN_TRAIT),
276 TraitItemKind::Type(..) => ("NodeTraitType", LABELS_CONST_IN_TRAIT),
277 },
278 HirNode::ImplItem(item) => match item.kind {
279 ImplItemKind::Fn(..) => ("Node::ImplItem", LABELS_FN_IN_IMPL),
280 ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL),
281 ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
282 },
283 _ => self
284 .tcx
285 .dcx()
286 .emit_fatal(diagnostics::UndefinedCleanDirty { span, kind: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", node))
})format!("{node:?}") }),
287 };
288 let labels =
289 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:?}"))));
290 (name, labels)
291 }
292
293 fn resolve_labels(&self, values: &[Symbol], span: Span) -> Labels {
294 let mut out = Labels::default();
295 for label in values {
296 let label_str = label.as_str();
297 if DepNode::has_label_string(label_str) {
298 if out.contains(label_str) {
299 self.tcx
300 .dcx()
301 .emit_fatal(diagnostics::RepeatedDepNodeLabel { span, label: label_str });
302 }
303 out.insert(label_str.to_string());
304 } else {
305 self.tcx
306 .dcx()
307 .emit_fatal(diagnostics::UnrecognizedDepNodeLabel { span, label: label_str });
308 }
309 }
310 out
311 }
312
313 fn dep_node_str(&self, dep_node: &DepNode) -> String {
314 if let Some(def_id) = dep_node.extract_def_id(self.tcx) {
315 ::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))
316 } else {
317 ::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)
318 }
319 }
320
321 fn assert_dirty(&self, item_span: Span, dep_node: DepNode) {
322 {
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:322",
"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(322u32),
::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);
323
324 if self.tcx.dep_graph.is_green(&dep_node) {
325 let dep_node_str = self.dep_node_str(&dep_node);
326 self.tcx
327 .dcx()
328 .emit_err(diagnostics::NotDirty { span: item_span, dep_node_str: &dep_node_str });
329 }
330 }
331
332 fn assert_clean(&self, item_span: Span, dep_node: DepNode) {
333 {
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:333",
"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(333u32),
::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);
334
335 if self.tcx.dep_graph.is_red(&dep_node) {
336 let dep_node_str = self.dep_node_str(&dep_node);
337 self.tcx
338 .dcx()
339 .emit_err(diagnostics::NotClean { span: item_span, dep_node_str: &dep_node_str });
340 }
341 }
342
343 fn check_item(&mut self, item_id: LocalDefId) {
344 let item_span = self.tcx.def_span(item_id.to_def_id());
345 let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id());
346
347 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 {
348 return;
349 };
350
351 for attr in clean_attrs {
352 let Some(assertion) = self.assertion_maybe(item_id, attr) else {
353 continue;
354 };
355 self.checked_attrs.insert(attr.span);
356 for label in assertion.clean.items().into_sorted_stable_ord() {
357 let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
358 self.assert_clean(item_span, dep_node);
359 }
360 for label in assertion.dirty.items().into_sorted_stable_ord() {
361 let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap();
362 self.assert_dirty(item_span, dep_node);
363 }
364 for label in assertion.loaded_from_disk.items().into_sorted_stable_ord() {
365 match DepNode::from_label_string(self.tcx, label, def_path_hash) {
366 Ok(dep_node) => {
367 if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) {
368 let dep_node_str = self.dep_node_str(&dep_node);
369 self.tcx.dcx().emit_err(diagnostics::NotLoaded {
370 span: item_span,
371 dep_node_str: &dep_node_str,
372 });
373 }
374 }
375 Err(()) => {
377 let dep_kind = dep_kind_from_label(label);
378 if !self.tcx.dep_graph.debug_dep_kind_was_loaded_from_disk(dep_kind) {
379 self.tcx.dcx().emit_err(diagnostics::NotLoaded {
380 span: item_span,
381 dep_node_str: &label,
382 });
383 }
384 }
385 }
386 }
387 }
388 }
389}
390
391struct FindAllAttrs<'tcx> {
395 tcx: TyCtxt<'tcx>,
396 found_attrs: Vec<&'tcx RustcCleanAttribute>,
397}
398
399impl<'tcx> FindAllAttrs<'tcx> {
400 fn is_active_attr(&self, attr: &RustcCleanAttribute) -> bool {
401 self.tcx.sess.config.contains(&(attr.cfg, None))
402 }
403
404 fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<Span>) {
405 for attr in &self.found_attrs {
406 if !checked_attrs.contains(&attr.span) {
407 self.tcx.dcx().emit_err(diagnostics::UncheckedClean { span: attr.span });
408 checked_attrs.insert(attr.span);
409 }
410 }
411 }
412}
413
414impl<'tcx> intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> {
415 type NestedFilter = nested_filter::All;
416
417 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
418 self.tcx
419 }
420
421 fn visit_attribute(&mut self, attr: &'tcx Attribute) {
422 if let Attribute::Parsed(AttributeKind::RustcClean(attrs)) = attr {
423 for attr in attrs {
424 if self.is_active_attr(attr) {
425 self.found_attrs.push(attr);
426 }
427 }
428 }
429 }
430}