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

Open
wants to merge 3 commits 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
15 changes: 14 additions & 1 deletion compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use rustc_ast::token::CommentKind;
use rustc_ast::{self as ast, AttrStyle};
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
use rustc_span::hygiene::Transparency;
use rustc_span::{Span, Symbol};
use rustc_span::{Ident, Span, Symbol};
use thin_vec::ThinVec;

use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
Expand Down Expand Up @@ -140,6 +140,13 @@ pub enum UsedBy {
Linker,
}

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

/// Represents parsed *built-in* inert attributes.
///
/// ## Overview
Expand Down Expand Up @@ -300,9 +307,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 @@ -42,7 +42,9 @@ impl AttributeKind {
LinkName { .. } => Yes,
LinkSection { .. } => No,
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::{Span, Symbol};
use rustc_span::{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, ());
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
4 changes: 2 additions & 2 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
}
})];

fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
fn finalize(self, cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
// FIXME(jdonszelmann): upgrade this list to *parsed* attributes
// once all of these have parsed forms. That'd make the check much nicer...
//
Expand Down Expand Up @@ -271,7 +271,7 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
fn finalize(self, _cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
// Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
Some(match (self.first_compiler, self.first_linker) {
(_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/confusables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
fn finalize(self, _cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.confusables.is_empty() {
return None;
}
Expand Down
105 changes: 105 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,105 @@
use rustc_attr_data_structures::{AttributeKind, MacroUseArgs};
use rustc_errors::DiagArgValue;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Ident, 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 {
/// All specific imports found so far
uses: ThinVec<Ident>,
/// Span of the first `#[macro_use]` arguments without arguments, used for linting
use_all: Option<Span>,
/// Spans of all `#[macro_use]` arguments with arguments, used for linting
uses_attr_spans: ThinVec<Span>,
/// Span of the first `#[macro_use]` argument, used as the span for this attribute
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 => {
// If there is a `#[macro_use]` import already, give a warning
if let Some(old_attr) = group.use_all.replace(span) {
cx.warn_unused_duplicate(old_attr, span);
}
}
ArgParser::List(list) => {
let mut arguments = ThinVec::new();

if list.is_empty() {
cx.warn_empty_attribute(list.span);
return;
}
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);
}

group.uses.extend(arguments);
}
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,
});
return;
}
};
},
)];

fn finalize(self, cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let arguments = if let Some(use_all) = self.use_all {
// If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
for specific_use in self.uses_attr_spans {
cx.warn_unused_duplicate(use_all, specific_use);
}
MacroUseArgs::UseAll
} else {
MacroUseArgs::UseSpecific(self.uses)
};
Some(AttributeKind::MacroUse { span: self.first_span?, arguments })
}
}
7 changes: 4 additions & 3 deletions compiler/rustc_attr_parsing/src/attributes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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 Expand Up @@ -84,7 +85,7 @@ pub(crate) trait AttributeParser<S: Stage>: Default + 'static {
/// that'd be equivalent to unconditionally applying an attribute to
/// every single syntax item that could have attributes applied to it.
/// Your accept mappings should determine whether this returns something.
fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind>;
fn finalize(self, cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've changed this to a mutable reference because I need a mutable reference to produce errors.
I'm assuming it not being mutable was just a mistake

}

/// Alternative to [`AttributeParser`] that automatically handles state management.
Expand Down Expand Up @@ -160,7 +161,7 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S>
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
fn finalize(self, _cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
Some(self.1?.0)
}
}
Expand Down Expand Up @@ -330,7 +331,7 @@ impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
fn finalize(self, _cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if let Some(first_span) = self.first_span {
Some(T::CONVERT(self.items, first_span))
} else {
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
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ impl AlignParser {
impl<S: Stage> AttributeParser<S> for AlignParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
fn finalize(self, _cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (align, span) = self.0?;
Some(AttributeKind::Align { align, span })
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_attr_parsing/src/attributes/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
),
];

fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
fn finalize(mut self, cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if let Some(atum) = self.allowed_through_unstable_modules {
if let Some((
Stability {
Expand Down Expand Up @@ -125,7 +125,7 @@ impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
fn finalize(self, _cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (stability, span) = self.stability?;

Some(AttributeKind::BodyStability { stability, span })
Expand Down Expand Up @@ -188,7 +188,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
}),
];

fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
fn finalize(mut self, cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.promotable {
if let Some((ref mut stab, _)) = self.stability {
stab.promotable = true;
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 @@ -29,6 +29,7 @@ use crate::attributes::link_attrs::{
};
use crate::attributes::lint_helpers::{AsPtrParser, 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 @@ -122,6 +123,7 @@ attribute_parsers!(
BodyStabilityParser,
ConfusablesParser,
ConstStabilityParser,
MacroUseParser,
NakedParser,
StabilityParser,
UsedParser,
Expand Down Expand Up @@ -166,6 +168,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 @@ -355,6 +358,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 @@ -525,6 +525,7 @@ pub(crate) enum AttributeParseErrorReason {
ExpectedNameValue(Option<Symbol>),
DuplicateKey(Symbol),
ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
ExpectedIdentifier,
}

pub(crate) struct AttributeParseError {
Expand Down Expand Up @@ -576,11 +577,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 @@ -618,6 +619,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 @@ -1304,6 +1304,7 @@ impl AttributeExt for Attribute {
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span,
a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
}
}
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 @@ -316,6 +316,8 @@ pub fn check_builtin_meta_item(
| sym::rustc_layout_scalar_valid_range_start
| sym::rustc_layout_scalar_valid_range_end
| sym::no_implicit_prelude
| sym::macro_use
| sym::macro_escape
) {
return;
}
Expand Down
Loading
Loading