From 94a0ba50e12740a68fa91a2890f63dcbaa0f9c33 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Wed, 4 Feb 2026 00:54:50 +0000 Subject: [PATCH] Port `rustc_clean` to attribute parser Also remove mentions of removed `rustc_dirty` --- Cargo.lock | 1 - .../src/attributes/rustc_internal.rs | 114 ++++++++++- compiler/rustc_attr_parsing/src/context.rs | 1 + .../rustc_hir/src/attrs/data_structures.rs | 23 ++- .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir/src/attrs/pretty_printing.rs | 4 +- compiler/rustc_incremental/Cargo.toml | 1 - .../rustc_incremental/src/assert_dep_graph.rs | 2 +- compiler/rustc_incremental/src/errors.rs | 32 +-- .../src/persist/{dirty_clean.rs => clean.rs} | 184 +++++++----------- compiler/rustc_incremental/src/persist/mod.rs | 2 +- .../rustc_incremental/src/persist/save.rs | 4 +- compiler/rustc_passes/messages.ftl | 5 +- compiler/rustc_passes/src/check_attr.rs | 15 +- compiler/rustc_passes/src/errors.rs | 4 +- compiler/rustc_query_system/src/ich/mod.rs | 1 - compiler/rustc_span/src/lib.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 - .../compiletest/src/runtest/incremental.rs | 2 +- .../incremental/{dirty_clean.rs => clean.rs} | 0 ...cked_dirty_clean.rs => unchecked_clean.rs} | 0 tests/ui/dep-graph/dep-graph-check-attr.rs | 4 +- .../ui/dep-graph/dep-graph-check-attr.stderr | 8 +- 23 files changed, 241 insertions(+), 169 deletions(-) rename compiler/rustc_incremental/src/persist/{dirty_clean.rs => clean.rs} (73%) rename tests/incremental/{dirty_clean.rs => clean.rs} (100%) rename tests/incremental/{unchecked_dirty_clean.rs => unchecked_clean.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index 85d3fc1aa7bf..6e088fc12d32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4051,7 +4051,6 @@ dependencies = [ "rustc_serialize", "rustc_session", "rustc_span", - "thin-vec", "tracing", ] diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 8961dc47706f..e30306d0791c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,7 +1,10 @@ use std::path::PathBuf; use rustc_ast::{LitIntType, LitKind, MetaItemLit}; -use rustc_hir::attrs::{BorrowckGraphvizFormatKind, RustcLayoutType, RustcMirKind}; +use rustc_hir::attrs::{ + BorrowckGraphvizFormatKind, RustcCleanAttribute, RustcCleanQueries, RustcLayoutType, + RustcMirKind, +}; use rustc_session::errors; use super::prelude::*; @@ -497,3 +500,112 @@ impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { ]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonConstTraitMethod; } + +pub(crate) struct RustcCleanParser; + +impl CombineAttributeParser for RustcCleanParser { + const PATH: &[Symbol] = &[sym::rustc_clean]; + + type Item = RustcCleanAttribute; + + const CONVERT: ConvertFn = |items, _| AttributeKind::RustcClean(items); + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + // tidy-alphabetical-start + Allow(Target::AssocConst), + Allow(Target::AssocTy), + Allow(Target::Const), + Allow(Target::Enum), + Allow(Target::Expression), + Allow(Target::Field), + Allow(Target::Fn), + Allow(Target::ForeignMod), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Mod), + Allow(Target::Static), + Allow(Target::Struct), + Allow(Target::Trait), + Allow(Target::TyAlias), + Allow(Target::Union), + // tidy-alphabetical-end + ]); + + const TEMPLATE: AttributeTemplate = + template!(List: &[r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span, args); + return None; + }; + let mut except = None; + let mut loaded_from_disk = None; + let mut cfg = None; + + for item in list.mixed() { + let Some((value, name)) = + item.meta_item().and_then(|m| Option::zip(m.args().name_value(), m.ident())) + else { + cx.expected_name_value(item.span(), None); + continue; + }; + let value_span = value.value_span; + let Some(value) = value.value_as_str() else { + cx.expected_string_literal(value_span, None); + continue; + }; + match name.name { + sym::cfg if cfg.is_some() => { + cx.duplicate_key(item.span(), sym::cfg); + } + + sym::cfg => { + cfg = Some(value); + } + sym::except if except.is_some() => { + cx.duplicate_key(item.span(), sym::except); + } + sym::except => { + let entries = + value.as_str().split(',').map(|s| Symbol::intern(s.trim())).collect(); + except = Some(RustcCleanQueries { entries, span: value_span }); + } + sym::loaded_from_disk if loaded_from_disk.is_some() => { + cx.duplicate_key(item.span(), sym::loaded_from_disk); + } + sym::loaded_from_disk => { + let entries = + value.as_str().split(',').map(|s| Symbol::intern(s.trim())).collect(); + loaded_from_disk = Some(RustcCleanQueries { entries, span: value_span }); + } + _ => { + cx.expected_specific_argument( + name.span, + &[sym::cfg, sym::except, sym::loaded_from_disk], + ); + } + } + } + let Some(cfg) = cfg else { + cx.expected_specific_argument(list.span, &[sym::cfg]); + return None; + }; + + Some(RustcCleanAttribute { + // Used for checking that all attributes have been checked + id: cx.cx.sess.psess.attr_id_generator.mk_attr_id(), + span: cx.attr_span, + cfg, + except, + loaded_from_disk, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index fa9f5b585926..e26595ed7545 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -153,6 +153,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, Combine, Combine, Combine, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index a53eff4637ff..7955183baf87 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -5,7 +5,7 @@ pub use ReprAttr::*; use rustc_abi::Align; pub use rustc_ast::attr::data_structures::*; use rustc_ast::token::DocFragmentKind; -use rustc_ast::{AttrStyle, ast}; +use rustc_ast::{AttrId, AttrStyle, ast}; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; @@ -716,6 +716,24 @@ pub enum BorrowckGraphvizFormatKind { TwoPhase, } +#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct RustcCleanAttribute { + pub id: AttrId, + pub span: Span, + pub cfg: Symbol, + pub except: Option, + pub loaded_from_disk: Option, +} + +/// Represents the `except=` or `loaded_from_disk=` argument of `#[rustc_clean]` +#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct RustcCleanQueries { + pub entries: ThinVec, + pub span: Span, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -1022,6 +1040,9 @@ pub enum AttributeKind { /// Represents `#[rustc_builtin_macro]`. RustcBuiltinMacro { builtin_name: Option, helper_attrs: ThinVec, span: Span }, + /// Represents `#[rustc_clean]` + RustcClean(ThinVec), + /// Represents `#[rustc_coherence_is_core]` RustcCoherenceIsCore(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 7ec1920152a5..500523567433 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -96,6 +96,7 @@ impl AttributeKind { RustcAsPtr(..) => Yes, RustcBodyStability { .. } => No, RustcBuiltinMacro { .. } => Yes, + RustcClean { .. } => No, RustcCoherenceIsCore(..) => No, RustcCoinductive(..) => No, RustcConfusables { .. } => Yes, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index bd268d2c423f..20efd72e20f7 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -6,7 +6,7 @@ use rustc_abi::Align; use rustc_ast::attr::data_structures::CfgEntry; use rustc_ast::attr::version::RustcVersion; use rustc_ast::token::{CommentKind, DocFragmentKind}; -use rustc_ast::{AttrStyle, IntTy, UintTy}; +use rustc_ast::{AttrId, AttrStyle, IntTy, UintTy}; use rustc_ast_pretty::pp::Printer; use rustc_data_structures::fx::FxIndexMap; use rustc_span::def_id::DefId; @@ -179,7 +179,7 @@ macro_rules! print_tup { } print_tup!(A B C D E F G H); -print_skip!(Span, (), ErrorGuaranteed); +print_skip!(Span, (), ErrorGuaranteed, AttrId); print_disp!(u8, u16, u128, usize, bool, NonZero, Limit); print_debug!( Symbol, diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml index ae96cc62e54a..d5c3ad2f8337 100644 --- a/compiler/rustc_incremental/Cargo.toml +++ b/compiler/rustc_incremental/Cargo.toml @@ -18,6 +18,5 @@ rustc_middle = { path = "../rustc_middle" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 0e04a2a784ec..7849741802c8 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -5,7 +5,7 @@ //! annotations. These annotations can be used to test whether paths //! exist in the graph. These checks run after codegen, so they view the //! the final state of the dependency graph. Note that there are -//! similar assertions found in `persist::dirty_clean` which check the +//! similar assertions found in `persist::clean` which check the //! **initial** state of the dependency graph, just after it has been //! loaded from disk. //! diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index 65109fdec03a..757392896d9e 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use rustc_macros::Diagnostic; -use rustc_span::{Ident, Span, Symbol}; +use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] #[diag("unrecognized `DepNode` variant: {$name}")] @@ -106,42 +106,12 @@ pub(crate) struct NotLoaded<'a> { pub dep_node_str: &'a str, } -#[derive(Diagnostic)] -#[diag("unknown `rustc_clean` argument")] -pub(crate) struct UnknownRustcCleanArgument { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag("no cfg attribute")] -pub(crate) struct NoCfg { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag("associated value expected for `{$ident}`")] -pub(crate) struct AssociatedValueExpectedFor { - #[primary_span] - pub span: Span, - pub ident: Ident, -} - -#[derive(Diagnostic)] -#[diag("expected an associated value")] -pub(crate) struct AssociatedValueExpected { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("found unchecked `#[rustc_clean]` attribute")] pub(crate) struct UncheckedClean { #[primary_span] pub span: Span, } - #[derive(Diagnostic)] #[diag("unable to delete old {$name} at `{$path}`: {$err}")] pub(crate) struct DeleteOld<'a> { diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/clean.rs similarity index 73% rename from compiler/rustc_incremental/src/persist/dirty_clean.rs rename to compiler/rustc_incremental/src/persist/clean.rs index 71fb18895246..c8ed91277aac 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/clean.rs @@ -19,26 +19,23 @@ //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. -use rustc_ast::{self as ast, MetaItemInner}; +use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::unord::UnordSet; +use rustc_hir::attrs::{AttributeKind, RustcCleanAttribute}; use rustc_hir::def_id::LocalDefId; use rustc_hir::{ - Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit, + Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, find_attr, + intravisit, }; use rustc_middle::dep_graph::{DepNode, DepNodeExt, dep_kind_from_label, label_strs}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; -use rustc_span::{Span, Symbol, sym}; -use thin_vec::ThinVec; +use rustc_span::{Span, Symbol}; use tracing::debug; use crate::errors; -const LOADED_FROM_DISK: Symbol = sym::loaded_from_disk; -const EXCEPT: Symbol = sym::except; -const CFG: Symbol = sym::cfg; - // Base and Extra labels to build up the labels /// For typedef, constants, and statics @@ -127,14 +124,14 @@ const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT]; type Labels = UnordSet; -/// Represents the requested configuration by rustc_clean/dirty +/// Represents the requested configuration by rustc_clean struct Assertion { clean: Labels, dirty: Labels, loaded_from_disk: Labels, } -pub(crate) fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { +pub(crate) fn check_clean_annotations(tcx: TyCtxt<'_>) { if !tcx.sess.opts.unstable_opts.query_dep_graph { return; } @@ -145,24 +142,24 @@ pub(crate) fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { } tcx.dep_graph.with_ignore(|| { - let mut dirty_clean_visitor = DirtyCleanVisitor { tcx, checked_attrs: Default::default() }; + let mut clean_visitor = CleanVisitor { tcx, checked_attrs: Default::default() }; let crate_items = tcx.hir_crate_items(()); for id in crate_items.free_items() { - dirty_clean_visitor.check_item(id.owner_id.def_id); + clean_visitor.check_item(id.owner_id.def_id); } for id in crate_items.trait_items() { - dirty_clean_visitor.check_item(id.owner_id.def_id); + clean_visitor.check_item(id.owner_id.def_id); } for id in crate_items.impl_items() { - dirty_clean_visitor.check_item(id.owner_id.def_id); + clean_visitor.check_item(id.owner_id.def_id); } for id in crate_items.foreign_items() { - dirty_clean_visitor.check_item(id.owner_id.def_id); + clean_visitor.check_item(id.owner_id.def_id); } let mut all_attrs = FindAllAttrs { tcx, found_attrs: vec![] }; @@ -171,67 +168,62 @@ pub(crate) fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { // Note that we cannot use the existing "unused attribute"-infrastructure // here, since that is running before codegen. This is also the reason why // all codegen-specific attributes are `AssumedUsed` in rustc_ast::feature_gate. - all_attrs.report_unchecked_attrs(dirty_clean_visitor.checked_attrs); + all_attrs.report_unchecked_attrs(clean_visitor.checked_attrs); }) } -struct DirtyCleanVisitor<'tcx> { +struct CleanVisitor<'tcx> { tcx: TyCtxt<'tcx>, checked_attrs: FxHashSet, } -impl<'tcx> DirtyCleanVisitor<'tcx> { - /// Possibly "deserialize" the attribute into a clean/dirty assertion - fn assertion_maybe(&mut self, item_id: LocalDefId, attr: &Attribute) -> Option { - assert!(attr.has_name(sym::rustc_clean)); - if !check_config(self.tcx, attr) { - // skip: not the correct `cfg=` - return None; - } - let assertion = self.assertion_auto(item_id, attr); - Some(assertion) +impl<'tcx> CleanVisitor<'tcx> { + /// Convert the attribute to an [`Assertion`] if the relevant cfg is active + fn assertion_maybe( + &mut self, + item_id: LocalDefId, + attr: &RustcCleanAttribute, + ) -> Option { + self.tcx + .sess + .psess + .config + .contains(&(attr.cfg, None)) + .then(|| self.assertion_auto(item_id, attr)) } /// Gets the "auto" assertion on pre-validated attr, along with the `except` labels. - fn assertion_auto(&mut self, item_id: LocalDefId, attr: &Attribute) -> Assertion { - let (name, mut auto) = self.auto_labels(item_id, attr); + fn assertion_auto(&mut self, item_id: LocalDefId, attr: &RustcCleanAttribute) -> Assertion { + let (name, mut auto) = self.auto_labels(item_id, attr.span); let except = self.except(attr); let loaded_from_disk = self.loaded_from_disk(attr); for e in except.items().into_sorted_stable_ord() { if !auto.remove(e) { - self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span(), name, e }); + self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); } } Assertion { clean: auto, dirty: except, loaded_from_disk } } /// `loaded_from_disk=` attribute value - fn loaded_from_disk(&self, attr: &Attribute) -> Labels { - for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { - if item.has_name(LOADED_FROM_DISK) { - let value = expect_associated_value(self.tcx, &item); - return self.resolve_labels(&item, value); - } - } - // If `loaded_from_disk=` is not specified, don't assert anything - Labels::default() + fn loaded_from_disk(&self, attr: &RustcCleanAttribute) -> Labels { + attr.loaded_from_disk + .as_ref() + .map(|queries| self.resolve_labels(&queries.entries, queries.span)) + .unwrap_or_default() } /// `except=` attribute value - fn except(&self, attr: &Attribute) -> Labels { - for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { - if item.has_name(EXCEPT) { - let value = expect_associated_value(self.tcx, &item); - return self.resolve_labels(&item, value); - } - } - // if no `label` or `except` is given, only the node's group are asserted - Labels::default() + fn except(&self, attr: &RustcCleanAttribute) -> Labels { + attr.except + .as_ref() + .map(|queries| self.resolve_labels(&queries.entries, queries.span)) + .unwrap_or_default() } /// Return all DepNode labels that should be asserted for this item. /// index=0 is the "name" used for error messages - fn auto_labels(&mut self, item_id: LocalDefId, attr: &Attribute) -> (&'static str, Labels) { + fn auto_labels(&mut self, item_id: LocalDefId, span: Span) -> (&'static str, Labels) { let node = self.tcx.hir_node_by_def_id(item_id); let (name, labels) = match node { HirNode::Item(item) => { @@ -282,7 +274,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL), _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem { - span: attr.span(), + span, kind: format!("{:?}", item.kind), }), } @@ -297,31 +289,31 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL), ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL), }, - _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirty { - span: attr.span(), - kind: format!("{node:?}"), - }), + _ => self + .tcx + .dcx() + .emit_fatal(errors::UndefinedCleanDirty { span, kind: format!("{node:?}") }), }; let labels = Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string()))); (name, labels) } - fn resolve_labels(&self, item: &MetaItemInner, value: Symbol) -> Labels { + fn resolve_labels(&self, values: &[Symbol], span: Span) -> Labels { let mut out = Labels::default(); - for label in value.as_str().split(',') { - let label = label.trim(); - if DepNode::has_label_string(label) { - if out.contains(label) { + for label in values { + let label_str = label.as_str(); + if DepNode::has_label_string(label_str) { + if out.contains(label_str) { self.tcx .dcx() - .emit_fatal(errors::RepeatedDepNodeLabel { span: item.span(), label }); + .emit_fatal(errors::RepeatedDepNodeLabel { span, label: label_str }); } - out.insert(label.to_string()); + out.insert(label_str.to_string()); } else { self.tcx .dcx() - .emit_fatal(errors::UnrecognizedDepNodeLabel { span: item.span(), label }); + .emit_fatal(errors::UnrecognizedDepNodeLabel { span, label: label_str }); } } out @@ -360,11 +352,18 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { fn check_item(&mut self, item_id: LocalDefId) { let item_span = self.tcx.def_span(item_id.to_def_id()); let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id()); - for attr in self.tcx.get_attrs(item_id, sym::rustc_clean) { + + let Some(attr) = + find_attr!(self.tcx.get_all_attrs(item_id), AttributeKind::RustcClean(attr) => attr) + else { + return; + }; + + for attr in attr { let Some(assertion) = self.assertion_maybe(item_id, attr) else { continue; }; - self.checked_attrs.insert(attr.id()); + self.checked_attrs.insert(attr.id); for label in assertion.clean.items().into_sorted_stable_ord() { let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); @@ -400,61 +399,24 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { } } -/// Given a `#[rustc_clean]` attribute, scan for a `cfg="foo"` attribute and check whether we have -/// a cfg flag called `foo`. -fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool { - debug!("check_config(attr={:?})", attr); - let config = &tcx.sess.psess.config; - debug!("check_config: config={:?}", config); - let mut cfg = None; - for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { - if item.has_name(CFG) { - let value = expect_associated_value(tcx, &item); - debug!("check_config: searching for cfg {:?}", value); - cfg = Some(config.contains(&(value, None))); - } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) { - tcx.dcx().emit_err(errors::UnknownRustcCleanArgument { span: item.span() }); - } - } - - match cfg { - None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span() }), - Some(c) => c, - } -} - -fn expect_associated_value(tcx: TyCtxt<'_>, item: &MetaItemInner) -> Symbol { - if let Some(value) = item.value_str() { - value - } else if let Some(ident) = item.ident() { - tcx.dcx().emit_fatal(errors::AssociatedValueExpectedFor { span: item.span(), ident }); - } else { - tcx.dcx().emit_fatal(errors::AssociatedValueExpected { span: item.span() }); - } -} - /// A visitor that collects all `#[rustc_clean]` attributes from /// the HIR. It is used to verify that we really ran checks for all annotated /// nodes. struct FindAllAttrs<'tcx> { tcx: TyCtxt<'tcx>, - found_attrs: Vec<&'tcx Attribute>, + found_attrs: Vec<&'tcx RustcCleanAttribute>, } impl<'tcx> FindAllAttrs<'tcx> { - fn is_active_attr(&mut self, attr: &Attribute) -> bool { - if attr.has_name(sym::rustc_clean) && check_config(self.tcx, attr) { - return true; - } - - false + fn is_active_attr(&self, attr: &RustcCleanAttribute) -> bool { + self.tcx.sess.psess.config.contains(&(attr.cfg, None)) } fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet) { for attr in &self.found_attrs { - if !checked_attrs.contains(&attr.id()) { - self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span() }); - checked_attrs.insert(attr.id()); + if !checked_attrs.contains(&attr.id) { + self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span }); + checked_attrs.insert(attr.id); } } } @@ -468,8 +430,12 @@ impl<'tcx> intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> { } fn visit_attribute(&mut self, attr: &'tcx Attribute) { - if self.is_active_attr(attr) { - self.found_attrs.push(attr); + if let Attribute::Parsed(AttributeKind::RustcClean(attrs)) = attr { + for attr in attrs { + if self.is_active_attr(attr) { + self.found_attrs.push(attr); + } + } } } } diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index f5d5167e0e2c..a3857967ab08 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -2,8 +2,8 @@ //! into the given directory. At the same time, it also hashes the //! various HIR nodes. +mod clean; mod data; -mod dirty_clean; mod file_format; mod fs; mod load; diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 58fea3278a83..be16b543e824 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -14,7 +14,7 @@ use tracing::debug; use super::data::*; use super::fs::*; -use super::{dirty_clean, file_format, work_product}; +use super::{clean, file_format, work_product}; use crate::assert_dep_graph::assert_dep_graph; use crate::errors; @@ -42,7 +42,7 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { let staging_dep_graph_path = staging_dep_graph_path(sess); sess.time("assert_dep_graph", || assert_dep_graph(tcx)); - sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx)); + sess.time("check_clean", || clean::check_clean_annotations(tcx)); join( move || { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index ab89af226793..d6d3d2a3e626 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -439,10 +439,11 @@ passes_rustc_allow_const_fn_unstable = attribute should be applied to `const fn` .label = not a `const fn` +passes_rustc_clean = + attribute requires -Z query-dep-graph to be enabled + passes_rustc_const_stable_indirect_pairing = `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied -passes_rustc_dirty_clean = - attribute requires -Z query-dep-graph to be enabled passes_rustc_force_inline_coro = attribute cannot be applied to a `async`, `gen` or `async gen` function diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4784456a59d9..cf2963e0c285 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -231,6 +231,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target) }, Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, + Attribute::Parsed(AttributeKind::RustcClean(attrs)) => { + for attr in attrs { + self.check_rustc_clean(attr.span); + } + } Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -356,10 +361,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_const, ..] => { self.check_diagnostic_on_const(attr.span(), hir_id, target, item) } - [sym::rustc_clean, ..] - | [sym::rustc_dirty, ..] - | [sym::rustc_if_this_changed, ..] - | [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr), + [sym::rustc_if_this_changed, ..] + | [sym::rustc_then_this_would_need, ..] => self.check_rustc_clean(attr.span()), [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } @@ -1262,9 +1265,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. - fn check_rustc_dirty_clean(&self, attr: &Attribute) { + fn check_rustc_clean(&self, span: Span) { if !self.tcx.sess.opts.unstable_opts.query_dep_graph { - self.dcx().emit_err(errors::RustcDirtyClean { span: attr.span() }); + self.dcx().emit_err(errors::RustcClean { span }); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 29688dd4d537..9eec6a5ed8b6 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -218,8 +218,8 @@ pub(crate) struct RustcLegacyConstGenericsIndexExceed { } #[derive(Diagnostic)] -#[diag(passes_rustc_dirty_clean)] -pub(crate) struct RustcDirtyClean { +#[diag(passes_rustc_clean)] +pub(crate) struct RustcClean { #[primary_span] pub span: Span, } diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_query_system/src/ich/mod.rs index 25778add60a9..72a7f3b8f970 100644 --- a/compiler/rustc_query_system/src/ich/mod.rs +++ b/compiler/rustc_query_system/src/ich/mod.rs @@ -11,7 +11,6 @@ pub const IGNORED_ATTRIBUTES: &[Symbol] = &[ sym::cfg_trace, // FIXME should this really be ignored? sym::rustc_if_this_changed, sym::rustc_then_this_would_need, - sym::rustc_dirty, sym::rustc_clean, sym::rustc_partition_reused, sym::rustc_partition_codegened, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index a178f3260d30..17930d35a251 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1331,6 +1331,7 @@ impl Default for Span { rustc_index::newtype_index! { #[orderable] #[debug_format = "AttrId({})"] + #[derive(HashStable_Generic)] pub struct AttrId {} } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c88dd0948b2a..aac4cf1de8c2 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1956,7 +1956,6 @@ symbols! { rustc_deprecated_safe_2024, rustc_diagnostic_item, rustc_diagnostic_macros, - rustc_dirty, rustc_do_not_const_check, rustc_doc_primitive, rustc_driver, diff --git a/src/tools/compiletest/src/runtest/incremental.rs b/src/tools/compiletest/src/runtest/incremental.rs index 44eb80300c39..5e7698e24858 100644 --- a/src/tools/compiletest/src/runtest/incremental.rs +++ b/src/tools/compiletest/src/runtest/incremental.rs @@ -21,7 +21,7 @@ impl TestCx<'_> { // - execute build/foo/bar.exe and save output // // FIXME -- use non-incremental mode as an oracle? That doesn't apply - // to #[rustc_dirty] and clean tests I guess + // to #[rustc_clean] tests I guess let revision = self.revision.expect("incremental tests require a list of revisions"); diff --git a/tests/incremental/dirty_clean.rs b/tests/incremental/clean.rs similarity index 100% rename from tests/incremental/dirty_clean.rs rename to tests/incremental/clean.rs diff --git a/tests/incremental/unchecked_dirty_clean.rs b/tests/incremental/unchecked_clean.rs similarity index 100% rename from tests/incremental/unchecked_dirty_clean.rs rename to tests/incremental/unchecked_clean.rs diff --git a/tests/ui/dep-graph/dep-graph-check-attr.rs b/tests/ui/dep-graph/dep-graph-check-attr.rs index a45bf24f8c1f..d1692a2166f8 100644 --- a/tests/ui/dep-graph/dep-graph-check-attr.rs +++ b/tests/ui/dep-graph/dep-graph-check-attr.rs @@ -5,7 +5,7 @@ #![allow(dead_code)] #![allow(unused_variables)] -#[rustc_clean(hir_owner)] //~ ERROR attribute requires -Z query-dep-graph +#[rustc_clean(cfg = "foo")] //~ ERROR attribute requires -Z query-dep-graph fn main() {} #[rustc_if_this_changed(hir_owner)] //~ ERROR attribute requires -Z query-dep-graph @@ -13,7 +13,7 @@ struct Foo { f: T, } -#[rustc_clean(hir_owner)] //~ ERROR attribute requires -Z query-dep-graph +#[rustc_clean(cfg = "foo")] //~ ERROR attribute requires -Z query-dep-graph type TypeAlias = Foo; #[rustc_then_this_would_need(variances_of)] //~ ERROR attribute requires -Z query-dep-graph diff --git a/tests/ui/dep-graph/dep-graph-check-attr.stderr b/tests/ui/dep-graph/dep-graph-check-attr.stderr index 46f4e4358cf6..35df92a5874e 100644 --- a/tests/ui/dep-graph/dep-graph-check-attr.stderr +++ b/tests/ui/dep-graph/dep-graph-check-attr.stderr @@ -1,8 +1,8 @@ error: attribute requires -Z query-dep-graph to be enabled --> $DIR/dep-graph-check-attr.rs:8:1 | -LL | #[rustc_clean(hir_owner)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[rustc_clean(cfg = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: attribute requires -Z query-dep-graph to be enabled --> $DIR/dep-graph-check-attr.rs:11:1 @@ -13,8 +13,8 @@ LL | #[rustc_if_this_changed(hir_owner)] error: attribute requires -Z query-dep-graph to be enabled --> $DIR/dep-graph-check-attr.rs:16:1 | -LL | #[rustc_clean(hir_owner)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[rustc_clean(cfg = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: attribute requires -Z query-dep-graph to be enabled --> $DIR/dep-graph-check-attr.rs:19:1