From 94a0ba50e12740a68fa91a2890f63dcbaa0f9c33 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Wed, 4 Feb 2026 00:54:50 +0000 Subject: [PATCH 1/3] 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 85d3fc1aa7bf7..6e088fc12d32d 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 8961dc47706f7..e30306d0791c2 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 fa9f5b5859262..e26595ed7545a 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 a53eff4637ff2..7955183baf874 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 7ec1920152a5e..5005235674335 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 bd268d2c423f7..20efd72e20f7f 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 ae96cc62e54a3..d5c3ad2f83370 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 0e04a2a784ec8..7849741802c8e 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 65109fdec03a7..757392896d9e4 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 71fb188952466..c8ed91277aac1 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 f5d5167e0e2cd..a3857967ab081 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 58fea3278a839..be16b543e824d 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 ab89af2267934..d6d3d2a3e6263 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 4784456a59d9c..cf2963e0c2853 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 29688dd4d5377..9eec6a5ed8b68 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 25778add60a98..72a7f3b8f970a 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 a178f3260d30c..17930d35a2515 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 c88dd0948b2a9..aac4cf1de8c2b 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 44eb80300c394..5e7698e24858b 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 a45bf24f8c1ff..d1692a2166f88 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 46f4e4358cf6a..35df92a5874ea 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 From de7067938dd7e88022f8d72bdd0a9167d4205650 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Wed, 4 Feb 2026 02:22:48 +0000 Subject: [PATCH 2/3] Port `rustc_if_this_changed`/`rustc_then_this_would_need` to attr parser --- Cargo.lock | 1 - .../src/attributes/rustc_internal.rs | 127 ++++++++++++++++-- compiler/rustc_attr_parsing/src/context.rs | 2 + .../rustc_hir/src/attrs/data_structures.rs | 9 +- .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 + compiler/rustc_incremental/Cargo.toml | 1 - .../rustc_incremental/src/assert_dep_graph.rs | 71 ++++------ compiler/rustc_incremental/src/errors.rs | 7 - .../rustc_incremental/src/persist/clean.rs | 11 +- compiler/rustc_passes/src/check_attr.rs | 5 +- compiler/rustc_span/src/lib.rs | 1 - .../dep-graph-assoc-type-codegen.stderr | 4 +- .../dep-graph/dep-graph-caller-callee.stderr | 8 +- tests/ui/dep-graph/dep-graph-check-attr.rs | 2 +- .../ui/dep-graph/dep-graph-check-attr.stderr | 4 +- .../dep-graph-struct-signature.stderr | 88 ++++++------ ...h-trait-impl-two-traits-same-method.stderr | 8 +- .../dep-graph-trait-impl-two-traits.stderr | 8 +- .../ui/dep-graph/dep-graph-trait-impl.stderr | 20 +-- .../ui/dep-graph/dep-graph-type-alias.stderr | 48 +++---- .../dep-graph/dep-graph-variance-alias.stderr | 4 +- 21 files changed, 256 insertions(+), 175 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e088fc12d32d..76f61fcb0d3e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4039,7 +4039,6 @@ name = "rustc_incremental" version = "0.0.0" dependencies = [ "rand 0.9.2", - "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_fs_util", diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index e30306d0791c2..37372995faf14 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -6,6 +6,7 @@ use rustc_hir::attrs::{ RustcMirKind, }; use rustc_session::errors; +use rustc_span::Symbol; use super::prelude::*; use super::util::parse_single_integer; @@ -237,7 +238,7 @@ impl NoArgsAttributeParser for RustcLintUntrackedQueryInformationPa pub(crate) struct RustcObjectLifetimeDefaultParser; impl SingleAttributeParser for RustcObjectLifetimeDefaultParser { - const PATH: &[rustc_span::Symbol] = &[sym::rustc_object_lifetime_default]; + const PATH: &[Symbol] = &[sym::rustc_object_lifetime_default]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); @@ -274,7 +275,7 @@ impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser pub(crate) struct RustcScalableVectorParser; impl SingleAttributeParser for RustcScalableVectorParser { - const PATH: &[rustc_span::Symbol] = &[sym::rustc_scalable_vector]; + const PATH: &[Symbol] = &[sym::rustc_scalable_vector]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); @@ -347,7 +348,7 @@ impl NoArgsAttributeParser for RustcOffloadKernelParser { pub(crate) struct RustcLayoutParser; impl CombineAttributeParser for RustcLayoutParser { - const PATH: &[rustc_span::Symbol] = &[sym::rustc_layout]; + const PATH: &[Symbol] = &[sym::rustc_layout]; type Item = RustcLayoutType; @@ -404,7 +405,7 @@ impl CombineAttributeParser for RustcLayoutParser { pub(crate) struct RustcMirParser; impl CombineAttributeParser for RustcMirParser { - const PATH: &[rustc_span::Symbol] = &[sym::rustc_mir]; + const PATH: &[Symbol] = &[sym::rustc_mir]; type Item = RustcMirKind; @@ -599,13 +600,115 @@ impl CombineAttributeParser for RustcCleanParser { 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, - }) + Some(RustcCleanAttribute { span: cx.attr_span, cfg, except, loaded_from_disk }) + } +} + +pub(crate) struct RustcIfThisChangedParser; + +impl SingleAttributeParser for RustcIfThisChangedParser { + const PATH: &[Symbol] = &[sym::rustc_if_this_changed]; + + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + + 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!(Word, List: &["DepNode"]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + match args { + ArgParser::NoArgs => Some(AttributeKind::RustcIfThisChanged(cx.attr_span, None)), + ArgParser::List(list) => { + let Some(item) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + let Some(ident) = item.meta_item().and_then(|item| item.ident()) else { + cx.expected_identifier(item.span()); + return None; + }; + Some(AttributeKind::RustcIfThisChanged(cx.attr_span, Some(ident.name))) + } + ArgParser::NameValue(_) => { + cx.expected_list_or_no_args(cx.inner_span); + None + } + } + } +} + +pub(crate) struct RustcThenThisWouldNeedParser; + +impl CombineAttributeParser for RustcThenThisWouldNeedParser { + const PATH: &[Symbol] = &[sym::rustc_then_this_would_need]; + type Item = Ident; + + const CONVERT: ConvertFn = + |items, span| AttributeKind::RustcThenThisWouldNeed(span, 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: &["DepNode"]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let Some(item) = args.list().and_then(|l| l.single()) else { + cx.expected_single_argument(cx.inner_span); + return None; + }; + let Some(ident) = item.meta_item().and_then(|item| item.ident()) else { + cx.expected_identifier(item.span()); + return None; + }; + Some(ident) } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index e26595ed7545a..065a17819393d 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -156,6 +156,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, Combine, Combine, // tidy-alphabetical-end @@ -192,6 +193,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 7955183baf874..ebb34ea870666 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::{AttrId, AttrStyle, ast}; +use rustc_ast::{AttrStyle, ast}; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; @@ -719,7 +719,6 @@ pub enum BorrowckGraphvizFormatKind { #[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, @@ -1098,6 +1097,9 @@ pub enum AttributeKind { /// Represents `#[rustc_hidden_type_of_opaques]` RustcHiddenTypeOfOpaques, + /// Represents `#[rustc_if_this_changed]` + RustcIfThisChanged(Span, Option), + /// Represents `#[rustc_layout]` RustcLayout(ThinVec), @@ -1199,6 +1201,9 @@ pub enum AttributeKind { /// Represents `#[rustc_std_internal_symbol]`. RustcStdInternalSymbol(Span), + /// Represents `#[rustc_then_this_would_need]` + RustcThenThisWouldNeed(Span, ThinVec), + /// Represents `#[rustc_unsafe_specialization_marker]`. RustcUnsafeSpecializationMarker(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 5005235674335..e97093383b2c1 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -113,6 +113,7 @@ impl AttributeKind { RustcDynIncompatibleTrait(..) => No, RustcHasIncoherentInherentImpls => Yes, RustcHiddenTypeOfOpaques => No, + RustcIfThisChanged(..) => No, RustcLayout(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, @@ -145,6 +146,7 @@ impl AttributeKind { RustcSkipDuringMethodDispatch { .. } => No, RustcSpecializationTrait(..) => No, RustcStdInternalSymbol(..) => No, + RustcThenThisWouldNeed(..) => No, RustcUnsafeSpecializationMarker(..) => No, RustcVariance => No, RustcVarianceOfOpaques => No, diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml index d5c3ad2f83370..ac4d7d8e1cbc0 100644 --- a/compiler/rustc_incremental/Cargo.toml +++ b/compiler/rustc_incremental/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start rand = "0.9.0" -rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fs_util = { path = "../rustc_fs_util" } diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 7849741802c8e..3447836598bf0 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -39,14 +39,16 @@ use std::io::Write; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::linked_graph::{Direction, INCOMING, NodeIndex, OUTGOING}; +use rustc_hir::Attribute; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; +use rustc_middle::bug; use rustc_middle::dep_graph::{ DepGraphQuery, DepKind, DepNode, DepNodeExt, DepNodeFilter, EdgeFilter, dep_kinds, }; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; -use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; use tracing::debug; use {rustc_graphviz as dot, rustc_hir as hir}; @@ -105,29 +107,13 @@ struct IfThisChanged<'tcx> { } impl<'tcx> IfThisChanged<'tcx> { - fn argument(&self, attr: &hir::Attribute) -> Option { - let mut value = None; - for list_item in attr.meta_item_list().unwrap_or_default() { - match list_item.ident() { - Some(ident) if list_item.is_word() && value.is_none() => value = Some(ident.name), - _ => - // FIXME better-encapsulate meta_item (don't directly access `node`) - { - span_bug!(list_item.span(), "unexpected meta-item {:?}", list_item) - } - } - } - value - } - fn process_attrs(&mut self, def_id: LocalDefId) { let def_path_hash = self.tcx.def_path_hash(def_id.to_def_id()); let hir_id = self.tcx.local_def_id_to_hir_id(def_id); let attrs = self.tcx.hir_attrs(hir_id); for attr in attrs { - if attr.has_name(sym::rustc_if_this_changed) { - let dep_node_interned = self.argument(attr); - let dep_node = match dep_node_interned { + if let Attribute::Parsed(AttributeKind::RustcIfThisChanged(span, dep_node)) = *attr { + let dep_node = match dep_node { None => DepNode::from_def_path_hash( self.tcx, def_path_hash, @@ -136,36 +122,29 @@ impl<'tcx> IfThisChanged<'tcx> { Some(n) => { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { Ok(n) => n, - Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode { - span: attr.span(), - name: n, - }), + Err(()) => self + .tcx + .dcx() + .emit_fatal(errors::UnrecognizedDepNode { span, name: n }), } } }; - self.if_this_changed.push((attr.span(), def_id.to_def_id(), dep_node)); - } else if attr.has_name(sym::rustc_then_this_would_need) { - let dep_node_interned = self.argument(attr); - let dep_node = match dep_node_interned { - Some(n) => { - match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { - Ok(n) => n, - Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode { - span: attr.span(), - name: n, - }), - } - } - None => { - self.tcx.dcx().emit_fatal(errors::MissingDepNode { span: attr.span() }); - } - }; - self.then_this_would_need.push(( - attr.span(), - dep_node_interned.unwrap(), - hir_id, - dep_node, - )); + self.if_this_changed.push((span, def_id.to_def_id(), dep_node)); + } else if let Attribute::Parsed(AttributeKind::RustcThenThisWouldNeed( + _, + ref dep_nodes, + )) = *attr + { + for &n in dep_nodes { + let Ok(dep_node) = + DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) + else { + self.tcx + .dcx() + .emit_fatal(errors::UnrecognizedDepNode { span: n.span, name: n.name }); + }; + self.then_this_would_need.push((n.span, n.name, hir_id, dep_node)); + } } } } diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index 757392896d9e4..9b33b49d4406a 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -11,13 +11,6 @@ pub(crate) struct UnrecognizedDepNode { pub name: Symbol, } -#[derive(Diagnostic)] -#[diag("missing `DepNode` variant")] -pub(crate) struct MissingDepNode { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("no `#[rustc_if_this_changed]` annotation detected")] pub(crate) struct MissingIfThisChanged { diff --git a/compiler/rustc_incremental/src/persist/clean.rs b/compiler/rustc_incremental/src/persist/clean.rs index c8ed91277aac1..d83ba5a78bed5 100644 --- a/compiler/rustc_incremental/src/persist/clean.rs +++ b/compiler/rustc_incremental/src/persist/clean.rs @@ -19,7 +19,6 @@ //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. -use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::unord::UnordSet; use rustc_hir::attrs::{AttributeKind, RustcCleanAttribute}; @@ -174,7 +173,7 @@ pub(crate) fn check_clean_annotations(tcx: TyCtxt<'_>) { struct CleanVisitor<'tcx> { tcx: TyCtxt<'tcx>, - checked_attrs: FxHashSet, + checked_attrs: FxHashSet, } impl<'tcx> CleanVisitor<'tcx> { @@ -363,7 +362,7 @@ impl<'tcx> CleanVisitor<'tcx> { let Some(assertion) = self.assertion_maybe(item_id, attr) else { continue; }; - self.checked_attrs.insert(attr.id); + self.checked_attrs.insert(attr.span); 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); @@ -412,11 +411,11 @@ impl<'tcx> FindAllAttrs<'tcx> { self.tcx.sess.psess.config.contains(&(attr.cfg, None)) } - fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet) { + fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet) { for attr in &self.found_attrs { - if !checked_attrs.contains(&attr.id) { + if !checked_attrs.contains(&attr.span) { self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span }); - checked_attrs.insert(attr.id); + checked_attrs.insert(attr.span); } } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cf2963e0c2853..48580508b4541 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -235,6 +235,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for attr in attrs { self.check_rustc_clean(attr.span); } + }, + Attribute::Parsed(AttributeKind::RustcIfThisChanged(span, _) | AttributeKind::RustcThenThisWouldNeed(span, _)) => { + self.check_rustc_clean(*span); } Attribute::Parsed( // tidy-alphabetical-start @@ -361,8 +364,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_const, ..] => { self.check_diagnostic_on_const(attr.span(), hir_id, target, item) } - [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) } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 17930d35a2515..a178f3260d30c 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1331,7 +1331,6 @@ impl Default for Span { rustc_index::newtype_index! { #[orderable] #[debug_format = "AttrId({})"] - #[derive(HashStable_Generic)] pub struct AttrId {} } diff --git a/tests/ui/dep-graph/dep-graph-assoc-type-codegen.stderr b/tests/ui/dep-graph/dep-graph-assoc-type-codegen.stderr index f26b43aa3ec74..b0372051f0264 100644 --- a/tests/ui/dep-graph/dep-graph-assoc-type-codegen.stderr +++ b/tests/ui/dep-graph/dep-graph-assoc-type-codegen.stderr @@ -1,8 +1,8 @@ error: OK - --> $DIR/dep-graph-assoc-type-codegen.rs:29:5 + --> $DIR/dep-graph-assoc-type-codegen.rs:29:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/dep-graph/dep-graph-caller-callee.stderr b/tests/ui/dep-graph/dep-graph-caller-callee.stderr index 4d06dc7f3ed37..33fe91b3500a6 100644 --- a/tests/ui/dep-graph/dep-graph-caller-callee.stderr +++ b/tests/ui/dep-graph/dep-graph-caller-callee.stderr @@ -1,14 +1,14 @@ error: OK - --> $DIR/dep-graph-caller-callee.rs:21:5 + --> $DIR/dep-graph-caller-callee.rs:21:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: no path from `x` to `typeck` - --> $DIR/dep-graph-caller-callee.rs:32:5 + --> $DIR/dep-graph-caller-callee.rs:32:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/dep-graph/dep-graph-check-attr.rs b/tests/ui/dep-graph/dep-graph-check-attr.rs index d1692a2166f88..c0697a5316f7d 100644 --- a/tests/ui/dep-graph/dep-graph-check-attr.rs +++ b/tests/ui/dep-graph/dep-graph-check-attr.rs @@ -8,7 +8,7 @@ #[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 +#[rustc_if_this_changed] //~ ERROR attribute requires -Z query-dep-graph struct Foo { f: T, } diff --git a/tests/ui/dep-graph/dep-graph-check-attr.stderr b/tests/ui/dep-graph/dep-graph-check-attr.stderr index 35df92a5874ea..4b651c47ac83a 100644 --- a/tests/ui/dep-graph/dep-graph-check-attr.stderr +++ b/tests/ui/dep-graph/dep-graph-check-attr.stderr @@ -7,8 +7,8 @@ LL | #[rustc_clean(cfg = "foo")] error: attribute requires -Z query-dep-graph to be enabled --> $DIR/dep-graph-check-attr.rs:11:1 | -LL | #[rustc_if_this_changed(hir_owner)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[rustc_if_this_changed] + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: attribute requires -Z query-dep-graph to be enabled --> $DIR/dep-graph-check-attr.rs:16:1 diff --git a/tests/ui/dep-graph/dep-graph-struct-signature.stderr b/tests/ui/dep-graph/dep-graph-struct-signature.stderr index cfe1e62d9318f..98efedc7244c8 100644 --- a/tests/ui/dep-graph/dep-graph-struct-signature.stderr +++ b/tests/ui/dep-graph/dep-graph-struct-signature.stderr @@ -1,134 +1,134 @@ error: no path from `WillChange` to `type_of` - --> $DIR/dep-graph-struct-signature.rs:28:5 + --> $DIR/dep-graph-struct-signature.rs:28:34 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: no path from `WillChange` to `associated_item` - --> $DIR/dep-graph-struct-signature.rs:29:5 + --> $DIR/dep-graph-struct-signature.rs:29:34 | LL | #[rustc_then_this_would_need(associated_item)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ error: no path from `WillChange` to `trait_def` - --> $DIR/dep-graph-struct-signature.rs:30:5 + --> $DIR/dep-graph-struct-signature.rs:30:34 | LL | #[rustc_then_this_would_need(trait_def)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:36:5 + --> $DIR/dep-graph-struct-signature.rs:36:34 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:37:5 + --> $DIR/dep-graph-struct-signature.rs:37:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:40:5 + --> $DIR/dep-graph-struct-signature.rs:40:34 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:41:5 + --> $DIR/dep-graph-struct-signature.rs:41:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:46:5 + --> $DIR/dep-graph-struct-signature.rs:46:34 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:53:5 + --> $DIR/dep-graph-struct-signature.rs:53:34 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:61:9 + --> $DIR/dep-graph-struct-signature.rs:61:38 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:63:9 + --> $DIR/dep-graph-struct-signature.rs:63:38 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: no path from `WillChange` to `type_of` - --> $DIR/dep-graph-struct-signature.rs:68:5 + --> $DIR/dep-graph-struct-signature.rs:68:34 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: no path from `WillChange` to `type_of` - --> $DIR/dep-graph-struct-signature.rs:75:5 + --> $DIR/dep-graph-struct-signature.rs:75:34 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: no path from `WillChange` to `fn_sig` - --> $DIR/dep-graph-struct-signature.rs:81:5 + --> $DIR/dep-graph-struct-signature.rs:81:34 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: no path from `WillChange` to `fn_sig` - --> $DIR/dep-graph-struct-signature.rs:84:5 + --> $DIR/dep-graph-struct-signature.rs:84:34 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: no path from `WillChange` to `typeck` - --> $DIR/dep-graph-struct-signature.rs:85:5 + --> $DIR/dep-graph-struct-signature.rs:85:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:32:9 + --> $DIR/dep-graph-struct-signature.rs:32:38 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: no path from `WillChange` to `fn_sig` - --> $DIR/dep-graph-struct-signature.rs:77:9 + --> $DIR/dep-graph-struct-signature.rs:77:38 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:48:9 + --> $DIR/dep-graph-struct-signature.rs:48:38 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:49:9 + --> $DIR/dep-graph-struct-signature.rs:49:38 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:55:9 + --> $DIR/dep-graph-struct-signature.rs:55:38 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-struct-signature.rs:56:9 + --> $DIR/dep-graph-struct-signature.rs:56:38 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: aborting due to 22 previous errors diff --git a/tests/ui/dep-graph/dep-graph-trait-impl-two-traits-same-method.stderr b/tests/ui/dep-graph/dep-graph-trait-impl-two-traits-same-method.stderr index 6f56cbc8dd7ae..293f918a31875 100644 --- a/tests/ui/dep-graph/dep-graph-trait-impl-two-traits-same-method.stderr +++ b/tests/ui/dep-graph/dep-graph-trait-impl-two-traits-same-method.stderr @@ -1,14 +1,14 @@ error: OK - --> $DIR/dep-graph-trait-impl-two-traits-same-method.rs:33:5 + --> $DIR/dep-graph-trait-impl-two-traits-same-method.rs:33:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: no path from `x::` to `typeck` - --> $DIR/dep-graph-trait-impl-two-traits-same-method.rs:42:5 + --> $DIR/dep-graph-trait-impl-two-traits-same-method.rs:42:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/dep-graph/dep-graph-trait-impl-two-traits.stderr b/tests/ui/dep-graph/dep-graph-trait-impl-two-traits.stderr index 08f382cc024c7..46cb0e9ea86fa 100644 --- a/tests/ui/dep-graph/dep-graph-trait-impl-two-traits.stderr +++ b/tests/ui/dep-graph/dep-graph-trait-impl-two-traits.stderr @@ -1,14 +1,14 @@ error: no path from `x::` to `typeck` - --> $DIR/dep-graph-trait-impl-two-traits.rs:32:5 + --> $DIR/dep-graph-trait-impl-two-traits.rs:32:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: no path from `x::` to `typeck` - --> $DIR/dep-graph-trait-impl-two-traits.rs:41:5 + --> $DIR/dep-graph-trait-impl-two-traits.rs:41:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/dep-graph/dep-graph-trait-impl.stderr b/tests/ui/dep-graph/dep-graph-trait-impl.stderr index bfee6d5c87b34..a5fce64c3a1c4 100644 --- a/tests/ui/dep-graph/dep-graph-trait-impl.stderr +++ b/tests/ui/dep-graph/dep-graph-trait-impl.stderr @@ -1,32 +1,32 @@ error: OK - --> $DIR/dep-graph-trait-impl.rs:28:5 + --> $DIR/dep-graph-trait-impl.rs:28:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-trait-impl.rs:33:5 + --> $DIR/dep-graph-trait-impl.rs:33:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-trait-impl.rs:38:5 + --> $DIR/dep-graph-trait-impl.rs:38:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-trait-impl.rs:43:5 + --> $DIR/dep-graph-trait-impl.rs:43:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: no path from `x::` to `typeck` - --> $DIR/dep-graph-trait-impl.rs:56:5 + --> $DIR/dep-graph-trait-impl.rs:56:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: aborting due to 5 previous errors diff --git a/tests/ui/dep-graph/dep-graph-type-alias.stderr b/tests/ui/dep-graph/dep-graph-type-alias.stderr index 42ac803b22ece..9f24c1113b982 100644 --- a/tests/ui/dep-graph/dep-graph-type-alias.stderr +++ b/tests/ui/dep-graph/dep-graph-type-alias.stderr @@ -1,74 +1,74 @@ error: no path from `TypeAlias` to `type_of` - --> $DIR/dep-graph-type-alias.rs:18:1 + --> $DIR/dep-graph-type-alias.rs:18:30 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: OK - --> $DIR/dep-graph-type-alias.rs:20:5 + --> $DIR/dep-graph-type-alias.rs:20:34 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: no path from `TypeAlias` to `type_of` - --> $DIR/dep-graph-type-alias.rs:25:1 + --> $DIR/dep-graph-type-alias.rs:25:30 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: OK - --> $DIR/dep-graph-type-alias.rs:28:9 + --> $DIR/dep-graph-type-alias.rs:28:38 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: no path from `TypeAlias` to `type_of` - --> $DIR/dep-graph-type-alias.rs:34:1 + --> $DIR/dep-graph-type-alias.rs:34:30 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: no path from `TypeAlias` to `type_of` - --> $DIR/dep-graph-type-alias.rs:42:1 + --> $DIR/dep-graph-type-alias.rs:42:30 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: OK - --> $DIR/dep-graph-type-alias.rs:49:1 + --> $DIR/dep-graph-type-alias.rs:49:30 | LL | #[rustc_then_this_would_need(type_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: OK - --> $DIR/dep-graph-type-alias.rs:52:1 + --> $DIR/dep-graph-type-alias.rs:52:30 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-type-alias.rs:53:1 + --> $DIR/dep-graph-type-alias.rs:53:30 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-type-alias.rs:36:5 + --> $DIR/dep-graph-type-alias.rs:36:34 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-type-alias.rs:44:5 + --> $DIR/dep-graph-type-alias.rs:44:34 | LL | #[rustc_then_this_would_need(fn_sig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: OK - --> $DIR/dep-graph-type-alias.rs:45:5 + --> $DIR/dep-graph-type-alias.rs:45:34 | LL | #[rustc_then_this_would_need(typeck)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: aborting due to 12 previous errors diff --git a/tests/ui/dep-graph/dep-graph-variance-alias.stderr b/tests/ui/dep-graph/dep-graph-variance-alias.stderr index e11de2452899f..83ef573aad484 100644 --- a/tests/ui/dep-graph/dep-graph-variance-alias.stderr +++ b/tests/ui/dep-graph/dep-graph-variance-alias.stderr @@ -1,8 +1,8 @@ error: OK - --> $DIR/dep-graph-variance-alias.rs:19:1 + --> $DIR/dep-graph-variance-alias.rs:19:30 | LL | #[rustc_then_this_would_need(variances_of)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: aborting due to 1 previous error From 522778e34826c70576163a207c641483e277419d Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Wed, 4 Feb 2026 20:43:29 +0000 Subject: [PATCH 3/3] Move `query-dep-graph` opt check to attr parsing --- .../src/attributes/rustc_internal.rs | 11 ++++++++++- .../src/session_diagnostics.rs | 8 ++++++++ compiler/rustc_passes/messages.ftl | 3 --- compiler/rustc_passes/src/check_attr.rs | 19 +++---------------- compiler/rustc_passes/src/errors.rs | 7 ------- 5 files changed, 21 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 37372995faf14..348882dc59fcd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -10,7 +10,7 @@ use rustc_span::Symbol; use super::prelude::*; use super::util::parse_single_integer; -use crate::session_diagnostics::RustcScalableVectorCountOutOfRange; +use crate::session_diagnostics::{AttributeRequiresOpt, RustcScalableVectorCountOutOfRange}; pub(crate) struct RustcMainParser; @@ -543,6 +543,9 @@ impl CombineAttributeParser for RustcCleanParser { cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser, ) -> impl IntoIterator { + if !cx.cx.sess.opts.unstable_opts.query_dep_graph { + cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" }); + } let Some(list) = args.list() else { cx.expected_list(cx.attr_span, args); return None; @@ -641,6 +644,9 @@ impl SingleAttributeParser for RustcIfThisChangedParser { const TEMPLATE: AttributeTemplate = template!(Word, List: &["DepNode"]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + if !cx.cx.sess.opts.unstable_opts.query_dep_graph { + cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" }); + } match args { ArgParser::NoArgs => Some(AttributeKind::RustcIfThisChanged(cx.attr_span, None)), ArgParser::List(list) => { @@ -701,6 +707,9 @@ impl CombineAttributeParser for RustcThenThisWouldNeedParser { cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser, ) -> impl IntoIterator { + if !cx.cx.sess.opts.unstable_opts.query_dep_graph { + cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" }); + } let Some(item) = args.list().and_then(|l| l.single()) else { cx.expected_single_argument(cx.inner_span); return None; diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index b0a334210f746..6ca47e8d698b1 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -532,6 +532,14 @@ pub(crate) struct RustcScalableVectorCountOutOfRange { pub n: u128, } +#[derive(Diagnostic)] +#[diag("attribute requires {$opt} to be enabled")] +pub(crate) struct AttributeRequiresOpt { + #[primary_span] + pub span: Span, + pub opt: &'static str, +} + pub(crate) enum AttributeParseErrorReason<'a> { ExpectedNoArgs, ExpectedStringLiteral { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index d6d3d2a3e6263..fae0052ab7a43 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -439,9 +439,6 @@ 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 diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 48580508b4541..c46321788aed7 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -231,14 +231,6 @@ 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(AttributeKind::RustcIfThisChanged(span, _) | AttributeKind::RustcThenThisWouldNeed(span, _)) => { - self.check_rustc_clean(*span); - } Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -300,6 +292,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcAsPtr(..) | AttributeKind::RustcBodyStability { .. } | AttributeKind::RustcBuiltinMacro { .. } + | AttributeKind::RustcClean(..) | AttributeKind::RustcCoherenceIsCore(..) | AttributeKind::RustcCoinductive(..) | AttributeKind::RustcConfusables { .. } @@ -315,6 +308,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcDynIncompatibleTrait(..) | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::RustcHiddenTypeOfOpaques + | AttributeKind::RustcIfThisChanged(..) | AttributeKind::RustcLayout(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) | AttributeKind::RustcLayoutScalarValidRangeStart(..) @@ -343,6 +337,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcSkipDuringMethodDispatch { .. } | AttributeKind::RustcSpecializationTrait(..) | AttributeKind::RustcStdInternalSymbol (..) + | AttributeKind::RustcThenThisWouldNeed(..) | AttributeKind::RustcUnsafeSpecializationMarker(..) | AttributeKind::RustcVariance | AttributeKind::RustcVarianceOfOpaques @@ -1264,14 +1259,6 @@ 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_clean(&self, span: Span) { - if !self.tcx.sess.opts.unstable_opts.query_dep_graph { - self.dcx().emit_err(errors::RustcClean { span }); - } - } - /// Checks if the `#[repr]` attributes on `item` are valid. fn check_repr( &self, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 9eec6a5ed8b68..d0fa1c4f0e037 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -217,13 +217,6 @@ pub(crate) struct RustcLegacyConstGenericsIndexExceed { pub arg_count: usize, } -#[derive(Diagnostic)] -#[diag(passes_rustc_clean)] -pub(crate) struct RustcClean { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(passes_repr_conflicting, code = E0566)] pub(crate) struct ReprConflicting {