Port rustc_clean to attribute parser

Also remove mentions of removed `rustc_dirty`
This commit is contained in:
Jamie Hill-Daniel
2026-02-04 00:54:50 +00:00
parent db3e99bbab
commit 94a0ba50e1
23 changed files with 241 additions and 169 deletions
-1
View File
@@ -4051,7 +4051,6 @@ dependencies = [
"rustc_serialize",
"rustc_session",
"rustc_span",
"thin-vec",
"tracing",
]
@@ -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<S: Stage> NoArgsAttributeParser<S> for RustcNonConstTraitMethodParser {
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonConstTraitMethod;
}
pub(crate) struct RustcCleanParser;
impl<S: Stage> CombineAttributeParser<S> for RustcCleanParser {
const PATH: &[Symbol] = &[sym::rustc_clean];
type Item = RustcCleanAttribute;
const CONVERT: ConvertFn<Self::Item> = |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<Item = Self::Item> {
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,
})
}
}
@@ -153,6 +153,7 @@ mod late {
Combine<ForceTargetFeatureParser>,
Combine<LinkParser>,
Combine<ReprParser>,
Combine<RustcCleanParser>,
Combine<RustcLayoutParser>,
Combine<RustcMirParser>,
Combine<TargetFeatureParser>,
@@ -5,7 +5,7 @@
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<RustcCleanQueries>,
pub loaded_from_disk: Option<RustcCleanQueries>,
}
/// 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<Symbol>,
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<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span },
/// Represents `#[rustc_clean]`
RustcClean(ThinVec<RustcCleanAttribute>),
/// Represents `#[rustc_coherence_is_core]`
RustcCoherenceIsCore(Span),
@@ -96,6 +96,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
RustcAsPtr(..) => Yes,
RustcBodyStability { .. } => No,
RustcBuiltinMacro { .. } => Yes,
RustcClean { .. } => No,
RustcCoherenceIsCore(..) => No,
RustcCoinductive(..) => No,
RustcConfusables { .. } => Yes,
@@ -6,7 +6,7 @@
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 @@ fn print_attribute(&self, p: &mut Printer) {
}
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<u32>, Limit);
print_debug!(
Symbol,
-1
View File
@@ -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
@@ -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.
//!
+1 -31
View File
@@ -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> {
@@ -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 @@
type Labels = UnordSet<String>;
/// 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<ast::AttrId>,
}
impl<'tcx> DirtyCleanVisitor<'tcx> {
/// Possibly "deserialize" the attribute into a clean/dirty assertion
fn assertion_maybe(&mut self, item_id: LocalDefId, attr: &Attribute) -> Option<Assertion> {
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<Assertion> {
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 @@ fn auto_labels(&mut self, item_id: LocalDefId, attr: &Attribute) -> (&'static st
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 @@ fn auto_labels(&mut self, item_id: LocalDefId, attr: &Attribute) -> (&'static st
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 @@ fn assert_clean(&self, item_span: Span, dep_node: DepNode) {
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 @@ fn check_item(&mut self, item_id: LocalDefId) {
}
}
/// 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<ast::AttrId>) {
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 @@ fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
}
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);
}
}
}
}
}
@@ -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;
@@ -14,7 +14,7 @@
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 || {
+3 -2
View File
@@ -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
+9 -6
View File
@@ -231,6 +231,11 @@ fn check_attributes(
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 @@ fn check_attributes(
[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 @@ fn check_rustc_legacy_const_generics(
/// 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 });
}
}
+2 -2
View File
@@ -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,
}
@@ -11,7 +11,6 @@
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,
+1
View File
@@ -1331,6 +1331,7 @@ fn default() -> Self {
rustc_index::newtype_index! {
#[orderable]
#[debug_format = "AttrId({})"]
#[derive(HashStable_Generic)]
pub struct AttrId {}
}
-1
View File
@@ -1956,7 +1956,6 @@
rustc_deprecated_safe_2024,
rustc_diagnostic_item,
rustc_diagnostic_macros,
rustc_dirty,
rustc_do_not_const_check,
rustc_doc_primitive,
rustc_driver,
@@ -21,7 +21,7 @@ pub(super) fn run_incremental_test(&self) {
// - 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");
+2 -2
View File
@@ -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<T> {
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<T> = Foo<T>;
#[rustc_then_this_would_need(variances_of)] //~ ERROR attribute requires -Z query-dep-graph
@@ -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