mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Port rustc_clean to attribute parser
Also remove mentions of removed `rustc_dirty`
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,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> {
|
||||
|
||||
+75
-109
@@ -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 || {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1331,6 +1331,7 @@ fn default() -> Self {
|
||||
rustc_index::newtype_index! {
|
||||
#[orderable]
|
||||
#[debug_format = "AttrId({})"]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub struct AttrId {}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user