Skip to content

Port #[crate_name] to the new attribute parsing infrastructure #137729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3803,7 +3803,6 @@ dependencies = [
"rustc_error_messages",
"rustc_fluent_macro",
"rustc_hashes",
"rustc_hir_id",
"rustc_index",
"rustc_lexer",
"rustc_lint_defs",
Expand Down Expand Up @@ -4139,7 +4138,7 @@ dependencies = [
"rustc_ast",
"rustc_data_structures",
"rustc_error_messages",
"rustc_hir_id",
"rustc_hir",
"rustc_macros",
"rustc_serialize",
"rustc_span",
Expand Down
34 changes: 34 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/crate_level.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use rustc_feature::{AttributeTemplate, template};
use rustc_hir::attrs::AttributeKind;
use rustc_span::{Symbol, sym};

use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
use crate::parser::ArgParser;

pub(crate) struct CrateNameParser;

impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
const PATH: &[Symbol] = &[sym::crate_name];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");

// FIXME: crate name is allowed on all targets and ignored,
// even though it should only be valid on crates of course
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let ArgParser::NameValue(n) = args else {
cx.expected_name_value(cx.attr_span, None);
return None;
};

let Some(name) = n.value_as_str() else {
cx.expected_string_literal(n.value_span, Some(n.value_as_lit()));
return None;
};
Comment on lines +27 to +30
Copy link
Member

@fmease fmease Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this regress #[crate_name = concat!("x")] fn f() {}? I guess it won't because concat! is already expanded by then?


Some(AttributeKind::CrateName { name, name_span: n.value_span, style: cx.attr_style })
}
}
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/attributes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub(crate) mod cfg;
pub(crate) mod cfg_old;
pub(crate) mod codegen_attrs;
pub(crate) mod confusables;
pub(crate) mod crate_level;
pub(crate) mod deprecation;
pub(crate) mod dummy;
pub(crate) mod inline;
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_attr_parsing/src/attributes/util.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name};
use rustc_ast::attr::AttributeExt;
use rustc_feature::is_builtin_attr_name;
use rustc_hir::RustcVersion;
use rustc_span::{Symbol, sym};
Expand All @@ -23,10 +23,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
}

pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
first_attr_value_str_by_name(attrs, sym::crate_name)
}

pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
attrs: impl Iterator<Item = &'tcx T>,
symbol: Symbol,
Expand Down
39 changes: 36 additions & 3 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use rustc_hir::{
AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId, MethodKind, Target,
};
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};

use crate::attributes::allow_unstable::{
Expand All @@ -25,6 +26,7 @@ use crate::attributes::codegen_attrs::{
TargetFeatureParser, TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::crate_level::CrateNameParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::dummy::DummyParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
Expand Down Expand Up @@ -66,6 +68,7 @@ use crate::attributes::traits::{
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
use crate::context::MaybeWarn::{Allow, Error, Warn};
use crate::lints::lint_name;
use crate::parser::{ArgParser, MetaItemParser, PathParser};
use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, InvalidTarget, UnknownMetaItem,
Expand Down Expand Up @@ -168,6 +171,7 @@ attribute_parsers!(

// tidy-alphabetical-start
Single<CoverageParser>,
Single<CrateNameParser>,
Single<CustomMirParser>,
Single<DeprecationParser>,
Single<DummyParser>,
Expand Down Expand Up @@ -315,7 +319,9 @@ pub struct AcceptContext<'f, 'sess, S: Stage> {
/// The span of the attribute currently being parsed
pub(crate) attr_span: Span,

/// Whether it is an inner or outer attribute
pub(crate) attr_style: AttrStyle,

/// The expected structure of the attribute.
///
/// Used in reporting errors to give a hint to users what the attribute *should* look like.
Expand Down Expand Up @@ -761,13 +767,34 @@ impl<'sess> AttributeParser<'sess, Early> {
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
) -> Option<Attribute> {
Self::parse_limited_should_emit(
sess,
attrs,
sym,
target_span,
target_node_id,
features,
ShouldEmit::Nothing,
)
}

/// Usually you want `parse_limited`, which defaults to no errors.
pub fn parse_limited_should_emit(
sess: &'sess Session,
attrs: &[ast::Attribute],
sym: Symbol,
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
should_emit: ShouldEmit,
) -> Option<Attribute> {
let mut p = Self {
features,
tools: Vec::new(),
parse_only: Some(sym),
sess,
stage: Early { emit_errors: ShouldEmit::Nothing },
stage: Early { emit_errors: should_emit },
};
let mut parsed = p.parse_attribute_list(
attrs,
Expand All @@ -776,8 +803,13 @@ impl<'sess> AttributeParser<'sess, Early> {
Target::Crate, // Does not matter, we're not going to emit errors anyways
OmitDoc::Skip,
std::convert::identity,
|_lint| {
panic!("can't emit lints here for now (nothing uses this atm)");
|AttributeLint { id, span, kind }| {
sess.psess.buffer_lint(
lint_name(&kind),
span,
id,
BuiltinLintDiag::AttributeLint(kind),
);
},
);
assert!(parsed.len() <= 1);
Expand Down Expand Up @@ -955,6 +987,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
target,
only: if only { "only " } else { "" },
applied,
span: attr.span,
},
});
}
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_attr_parsing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,8 @@ mod session_diagnostics;

pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
pub use attributes::cfg_old::*;
pub use attributes::util::{
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
};
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit};
pub use lints::emit_attribute_lint;
pub use lints::{decorate_attribute_lint_kind, emit_attribute_lint};

rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
105 changes: 57 additions & 48 deletions compiler/rustc_attr_parsing/src/lints.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,69 @@
use rustc_errors::{DiagArgValue, LintEmitter};
use rustc_errors::{AttributeLintDecorator, DeferredAttributeLintDecorator, DiagArgValue};
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_hir::{HirId, Target};
use rustc_session::lint::Lint;
use rustc_span::sym;

use crate::session_diagnostics;

pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emitter: L) {
let AttributeLint { id, span, kind } = lint;
pub(crate) fn lint_name(kind: &AttributeLintKind) -> &'static Lint {
use rustc_session::lint::builtin::*;
match kind {
AttributeLintKind::UnusedDuplicate { .. } => UNUSED_ATTRIBUTES,
AttributeLintKind::IllFormedAttributeInput { .. } => ILL_FORMED_ATTRIBUTE_INPUT,
AttributeLintKind::EmptyAttribute { .. } => UNUSED_ATTRIBUTES,
AttributeLintKind::InvalidTarget { name, target, .. } => {
// This check is here because `deprecated` had its own lint group and removing this would be a breaking change
if *name == sym::deprecated
&& ![Target::Closure, Target::Expression, Target::Statement, Target::Arm]
.contains(target)
{
rustc_session::lint::builtin::USELESS_DEPRECATED
} else {
rustc_session::lint::builtin::UNUSED_ATTRIBUTES
}
}
}
}

pub fn decorate_attribute_lint_kind<L: AttributeLintDecorator>(
kind: &AttributeLintKind,
lint_emitter: L,
) {
match kind {
&AttributeLintKind::UnusedDuplicate { this, other, warning } => lint_emitter
.emit_node_span_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
*id,
*span,
session_diagnostics::UnusedDuplicate { this, other, warning },
),
&AttributeLintKind::UnusedDuplicate { this, other, warning } => {
lint_emitter.decorate(session_diagnostics::UnusedDuplicate { this, other, warning })
}
AttributeLintKind::IllFormedAttributeInput { suggestions } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT,
*id,
*span,
session_diagnostics::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
},
);
lint_emitter.decorate(session_diagnostics::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
});
}
AttributeLintKind::EmptyAttribute { first_span } => lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
*id,
*first_span,
session_diagnostics::EmptyAttributeList { attr_span: *first_span },
),
&AttributeLintKind::InvalidTarget { name, target, ref applied, only } => lint_emitter
.emit_node_span_lint(
// This check is here because `deprecated` had its own lint group and removing this would be a breaking change
if name == sym::deprecated
&& ![Target::Closure, Target::Expression, Target::Statement, Target::Arm]
.contains(&target)
{
rustc_session::lint::builtin::USELESS_DEPRECATED
} else {
rustc_session::lint::builtin::UNUSED_ATTRIBUTES
},
*id,
*span,
session_diagnostics::InvalidTargetLint {
name,
target: target.plural_name(),
applied: applied.clone(),
only,
attr_span: *span,
},
),

AttributeLintKind::EmptyAttribute { first_span } => lint_emitter
.decorate(session_diagnostics::EmptyAttributeList { attr_span: *first_span }),
&AttributeLintKind::InvalidTarget { name, target, ref applied, only, span } => lint_emitter
.decorate(session_diagnostics::InvalidTargetLint {
name,
target: target.plural_name(),
applied: applied.clone(),
only,
attr_span: span,
}),
}
}

pub fn emit_attribute_lint<L: DeferredAttributeLintDecorator<ID = HirId>>(
lint: &AttributeLint<HirId>,
lint_emitter: L,
) {
let AttributeLint { id, span, kind } = lint;

let lint_name = lint_name(&lint.kind);

let emit = lint_emitter.prepare(lint_name, *id, *span);
decorate_attribute_lint_kind(kind, emit);
}
7 changes: 1 addition & 6 deletions compiler/rustc_errors/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ rustc_error_codes = { path = "../rustc_error_codes" }
rustc_error_messages = { path = "../rustc_error_messages" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hashes = { path = "../rustc_hashes" }
rustc_hir_id = { path = "../rustc_hir_id" }
rustc_index = { path = "../rustc_index" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
Expand All @@ -29,8 +28,4 @@ tracing = "0.1"

[target.'cfg(windows)'.dependencies.windows]
version = "0.61.0"
features = [
"Win32_Foundation",
"Win32_Security",
"Win32_System_Threading",
]
features = ["Win32_Foundation", "Win32_Security", "Win32_System_Threading"]
17 changes: 11 additions & 6 deletions compiler/rustc_errors/src/lib.rs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, AttributeLintDecoratorOrEmitter, decorate_or_emit, etc. would be more accurate.

Or going vague, AttributeLintHandler, fn handle, DeferredAttributeLintHandler, type Handler: …;.

But if you feel it's too churny to go rename them again, it's okay to leave it.

Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ pub use rustc_error_messages::{
fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display,
};
use rustc_hashes::Hash128;
use rustc_hir_id::HirId;
pub use rustc_lint_defs::{Applicability, listify, pluralize};
use rustc_lint_defs::{Lint, LintExpectationId};
use rustc_macros::{Decodable, Encodable};
Expand Down Expand Up @@ -105,17 +104,23 @@ rustc_data_structures::static_assert_size!(PResult<'_, ()>, 24);
#[cfg(target_pointer_width = "64")]
rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24);

pub trait AttributeLintDecorator {
fn decorate(self, diag: impl for<'a> LintDiagnostic<'a, ()>);
}

/// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`.
/// Always the `TyCtxt`.
pub trait LintEmitter: Copy {
pub trait DeferredAttributeLintDecorator: Copy {
type ID: Copy;
type Decorator: AttributeLintDecorator;

#[track_caller]
fn emit_node_span_lint(
fn prepare(
self,
lint: &'static Lint,
hir_id: HirId,
id: Self::ID,
span: impl Into<MultiSpan>,
decorator: impl for<'a> LintDiagnostic<'a, ()>,
);
) -> Self::Decorator;
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ pub enum AttributeKind {
/// Represents `#[coverage(..)]`.
Coverage(Span, CoverageAttrKind),

/// Represents `#[crate_name = ...]`
CrateName { name: Symbol, name_span: Span, style: AttrStyle },

/// Represents `#[custom_mir]`.
CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span),

Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ pub enum EncodeCrossCrate {
}

impl AttributeKind {
/// Whether this attribute should be encoded in metadata files.
///
/// If this is "Yes", then another crate can do `tcx.get_all_attrs(did)` for a did in this crate, and get the attribute.
/// When this is No, the attribute is filtered out while encoding and other crate won't be able to observe it.
/// This can be unexpectedly good for performance, so unless necessary for cross-crate compilation, prefer No.
pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
use AttributeKind::*;
use EncodeCrossCrate::*;
Expand All @@ -31,6 +36,7 @@ impl AttributeKind {
ConstTrait(..) => No,
Coroutine(..) => No,
Coverage(..) => No,
CrateName { .. } => No,
CustomMir(_, _, _) => Yes,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
Expand Down
Loading
Loading