Skip to content

Ports #[macro_use] and #[macro_escape] to the new attribute parsing infrastructure #143843

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

Merged
merged 2 commits into from
Jul 23, 2025
Merged
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
19 changes: 19 additions & 0 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,19 @@ pub enum UsedBy {
Linker,
}

#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum MacroUseArgs {
UseAll,
UseSpecific(ThinVec<Ident>),
}

impl Default for MacroUseArgs {
fn default() -> Self {
Self::UseSpecific(ThinVec::new())
}
}

#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
pub struct StrippedCfgItem<ModId = DefId> {
pub parent_module: ModId,
Expand Down Expand Up @@ -351,9 +364,15 @@ pub enum AttributeKind {
/// Represents `#[loop_match]`.
LoopMatch(Span),

/// Represents `#[macro_escape]`.
MacroEscape(Span),

/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),

/// Represents `#[macro_use]`.
MacroUse { span: Span, arguments: MacroUseArgs },

/// Represents `#[marker]`.
Marker(Span),

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ impl AttributeKind {
LinkOrdinal { .. } => No,
LinkSection { .. } => Yes, // Needed for rustdoc
LoopMatch(..) => No,
MacroEscape(..) => No,
MacroTransparency(..) => Yes,
MacroUse { .. } => No,
Marker(..) => No,
MayDangle(..) => No,
MustUse { .. } => Yes,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_attr_data_structures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use rustc_ast::token::CommentKind;
use rustc_ast::{AttrStyle, IntTy, UintTy};
use rustc_ast_pretty::pp::Printer;
use rustc_span::hygiene::Transparency;
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
pub use stability::*;
use thin_vec::ThinVec;
pub use version::*;
Expand Down Expand Up @@ -172,7 +172,7 @@ macro_rules! print_tup {
print_tup!(A B C D E F G H);
print_skip!(Span, (), ErrorGuaranteed);
print_disp!(u16, bool, NonZero<u32>);
print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);

/// Finds attributes in sequences of attributes by pattern matching.
///
Expand Down
115 changes: 115 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use rustc_attr_data_structures::{AttributeKind, MacroUseArgs};
use rustc_errors::DiagArgValue;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;

use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate};
use crate::context::{AcceptContext, FinalizeContext, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics;

pub(crate) struct MacroEscapeParser;
impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
const PATH: &[Symbol] = &[sym::macro_escape];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::MacroEscape;
}

/// `#[macro_use]` attributes can either:
/// - Use all macros from a crate, if provided without arguments
/// - Use specific macros from a crate, if provided with arguments `#[macro_use(macro1, macro2)]`
/// A warning should be provided if an use all is combined with specific uses, or if multiple use-alls are used.
#[derive(Default)]
pub(crate) struct MacroUseParser {
state: MacroUseArgs,

/// Spans of all `#[macro_use]` arguments with arguments, used for linting
uses_attr_spans: ThinVec<Span>,
/// If `state` is `UseSpecific`, stores the span of the first `#[macro_use]` argument, used as the span for this attribute
/// If `state` is `UseAll`, stores the span of the first `#[macro_use]` arguments without arguments
first_span: Option<Span>,
}

const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ...");

impl<S: Stage> AttributeParser<S> for MacroUseParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::macro_use],
MACRO_USE_TEMPLATE,
|group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| {
let span = cx.attr_span;
group.first_span.get_or_insert(span);
match args {
ArgParser::NoArgs => {
match group.state {
MacroUseArgs::UseAll => {
let first_span = group.first_span.expect(
"State is UseAll is some so this is not the first attribute",
);
// Since there is a `#[macro_use]` import already, give a warning
cx.warn_unused_duplicate(first_span, span);
}
MacroUseArgs::UseSpecific(_) => {
group.state = MacroUseArgs::UseAll;
group.first_span = Some(span);
// If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
for specific_use in group.uses_attr_spans.drain(..) {
cx.warn_unused_duplicate(span, specific_use);
}
}
}
}
ArgParser::List(list) => {
if list.is_empty() {
cx.warn_empty_attribute(list.span);
return;
}

match &mut group.state {
MacroUseArgs::UseAll => {
let first_span = group.first_span.expect(
"State is UseAll is some so this is not the first attribute",
);
cx.warn_unused_duplicate(first_span, span);
}
MacroUseArgs::UseSpecific(arguments) => {
// Store here so if we encounter a `UseAll` later we can still lint this attribute
group.uses_attr_spans.push(cx.attr_span);

for item in list.mixed() {
let Some(item) = item.meta_item() else {
cx.expected_identifier(item.span());
continue;
};
if let Err(err_span) = item.args().no_args() {
cx.expected_no_args(err_span);
continue;
}
let Some(item) = item.path().word() else {
cx.expected_identifier(item.span());
continue;
};
arguments.push(item);
}
}
}
}
ArgParser::NameValue(_) => {
let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
span,
});
}
}
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
}
}
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 @@ -36,6 +36,7 @@ pub(crate) mod inline;
pub(crate) mod link_attrs;
pub(crate) mod lint_helpers;
pub(crate) mod loop_match;
pub(crate) mod macro_attrs;
pub(crate) mod must_use;
pub(crate) mod no_implicit_prelude;
pub(crate) mod non_exhaustive;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/must_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
ArgParser::List(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
cx.emit_err(session_diagnostics::MustUseIllFormedAttributeInput {
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::attributes::lint_helpers::{
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
};
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser};
use crate::attributes::must_use::MustUseParser;
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
use crate::attributes::non_exhaustive::NonExhaustiveParser;
Expand Down Expand Up @@ -126,6 +127,7 @@ attribute_parsers!(
BodyStabilityParser,
ConfusablesParser,
ConstStabilityParser,
MacroUseParser,
NakedParser,
StabilityParser,
UsedParser,
Expand Down Expand Up @@ -174,6 +176,7 @@ attribute_parsers!(
Single<WithoutArgs<FfiPureParser>>,
Single<WithoutArgs<FundamentalParser>>,
Single<WithoutArgs<LoopMatchParser>>,
Single<WithoutArgs<MacroEscapeParser>>,
Single<WithoutArgs<MarkerParser>>,
Single<WithoutArgs<MayDangleParser>>,
Single<WithoutArgs<NoImplicitPreludeParser>>,
Expand Down Expand Up @@ -386,6 +389,17 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
})
}

/// emit an error that a `name` was expected here
pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIdentifier,
})
}

/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
/// a nicer error message talking about the specific name that was found lacking a value.
pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
Expand Down
10 changes: 7 additions & 3 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ pub(crate) struct IllFormedAttributeInput {

#[derive(Diagnostic)]
#[diag(attr_parsing_ill_formed_attribute_input)]
pub(crate) struct MustUseIllFormedAttributeInput {
pub(crate) struct IllFormedAttributeInputLint {
#[primary_span]
pub span: Span,
pub num_suggestions: usize,
Expand Down Expand Up @@ -549,6 +549,7 @@ pub(crate) enum AttributeParseErrorReason {
/// Should we tell the user to write a list when they didn't?
list: bool,
},
ExpectedIdentifier,
}

pub(crate) struct AttributeParseError {
Expand Down Expand Up @@ -600,11 +601,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
diag.code(E0538);
}
AttributeParseErrorReason::UnexpectedLiteral => {
diag.span_label(self.span, format!("didn't expect a literal here"));
diag.span_label(self.span, "didn't expect a literal here");
diag.code(E0565);
}
AttributeParseErrorReason::ExpectedNoArgs => {
diag.span_label(self.span, format!("didn't expect any arguments here"));
diag.span_label(self.span, "didn't expect any arguments here");
diag.code(E0565);
}
AttributeParseErrorReason::ExpectedNameValue(None) => {
Expand Down Expand Up @@ -684,6 +685,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
}
}
}
AttributeParseErrorReason::ExpectedIdentifier => {
diag.span_label(self.span, "expected a valid identifier here");
}
}

let suggestions = self.template.suggestions(false, &name);
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0466.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#### Note: this error code is no longer emitted by the compiler.

Macro import declaration was malformed.

Erroneous code examples:

```compile_fail,E0466
```compile_fail
#[macro_use(a_macro(another_macro))] // error: invalid import declaration
extern crate core as some_crate;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,7 @@ impl AttributeExt for Attribute {
// FIXME: should not be needed anymore when all attrs are parsed
Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span,
Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_parse/src/validate_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ pub fn check_builtin_meta_item(
| sym::cold
| sym::target_feature
| sym::rustc_allow_const_fn_unstable
| sym::macro_use
| sym::macro_escape
| sym::naked
| sym::no_mangle
| sym::non_exhaustive
Expand Down
17 changes: 8 additions & 9 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => {
self.check_link_section(hir_id, *attr_span, span, target)
}
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => {
self.check_macro_use(hir_id, sym::macro_use, *span, target)
}
Attribute::Parsed(AttributeKind::MacroEscape(span)) => {
self.check_macro_use(hir_id, sym::macro_escape, *span, target)
}
Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
self.check_naked(hir_id, *attr_span, span, target)
}
Expand Down Expand Up @@ -362,9 +368,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
self.check_macro_use(hir_id, attr, target)
}
[sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod),
[sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
[sym::should_panic, ..] => {
Expand Down Expand Up @@ -2411,17 +2414,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}

fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
let Some(name) = attr.name() else {
return;
};
fn check_macro_use(&self, hir_id: HirId, name: Symbol, attr_span: Span, target: Target) {
match target {
Target::ExternCrate | Target::Mod => {}
_ => {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
attr_span,
errors::MacroUse { name },
);
}
Expand Down Expand Up @@ -2474,7 +2474,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// Warn on useless empty attributes.
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
let note = if attr.has_any_name(&[
sym::macro_use,
sym::allow,
sym::expect,
sym::warn,
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_resolve/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion =
resolve_attributes_starting_with_rustc_are_reserved =
attributes starting with `rustc` are reserved for use by the `rustc` compiler

resolve_bad_macro_import = bad macro import

resolve_binding_in_never_pattern =
never patterns cannot contain variable bindings
.suggestion = use a wildcard `_` instead
Expand Down
Loading
Loading