Skip to content

Commit 4ff3fa0

Browse files
committed
Auto merge of #143843 - JonathanBrouwer:macro-use-parser, r=oli-obk
Ports `#[macro_use]` and `#[macro_escape]` to the new attribute parsing infrastructure Ports `#[macro_use]` and `#[macro_escape]` to the new attribute parsing infrastructure for #131229 (comment) r? `@jdonszelmann` `@oli-obk`
2 parents 5a30e43 + 3303534 commit 4ff3fa0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+480
-150
lines changed

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ pub enum UsedBy {
157157
Linker,
158158
}
159159

160+
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
161+
#[derive(HashStable_Generic, PrintAttribute)]
162+
pub enum MacroUseArgs {
163+
UseAll,
164+
UseSpecific(ThinVec<Ident>),
165+
}
166+
167+
impl Default for MacroUseArgs {
168+
fn default() -> Self {
169+
Self::UseSpecific(ThinVec::new())
170+
}
171+
}
172+
160173
#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
161174
pub struct StrippedCfgItem<ModId = DefId> {
162175
pub parent_module: ModId,
@@ -351,9 +364,15 @@ pub enum AttributeKind {
351364
/// Represents `#[loop_match]`.
352365
LoopMatch(Span),
353366

367+
/// Represents `#[macro_escape]`.
368+
MacroEscape(Span),
369+
354370
/// Represents `#[rustc_macro_transparency]`.
355371
MacroTransparency(Transparency),
356372

373+
/// Represents `#[macro_use]`.
374+
MacroUse { span: Span, arguments: MacroUseArgs },
375+
357376
/// Represents `#[marker]`.
358377
Marker(Span),
359378

compiler/rustc_attr_data_structures/src/encode_cross_crate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ impl AttributeKind {
4545
LinkOrdinal { .. } => No,
4646
LinkSection { .. } => Yes, // Needed for rustdoc
4747
LoopMatch(..) => No,
48+
MacroEscape(..) => No,
4849
MacroTransparency(..) => Yes,
50+
MacroUse { .. } => No,
4951
Marker(..) => No,
5052
MayDangle(..) => No,
5153
MustUse { .. } => Yes,

compiler/rustc_attr_data_structures/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_ast::token::CommentKind;
2424
use rustc_ast::{AttrStyle, IntTy, UintTy};
2525
use rustc_ast_pretty::pp::Printer;
2626
use rustc_span::hygiene::Transparency;
27-
use rustc_span::{ErrorGuaranteed, Span, Symbol};
27+
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
2828
pub use stability::*;
2929
use thin_vec::ThinVec;
3030
pub use version::*;
@@ -172,7 +172,7 @@ macro_rules! print_tup {
172172
print_tup!(A B C D E F G H);
173173
print_skip!(Span, (), ErrorGuaranteed);
174174
print_disp!(u16, bool, NonZero<u32>);
175-
print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
175+
print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
176176

177177
/// Finds attributes in sequences of attributes by pattern matching.
178178
///
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use rustc_attr_data_structures::{AttributeKind, MacroUseArgs};
2+
use rustc_errors::DiagArgValue;
3+
use rustc_feature::{AttributeTemplate, template};
4+
use rustc_span::{Span, Symbol, sym};
5+
use thin_vec::ThinVec;
6+
7+
use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate};
8+
use crate::context::{AcceptContext, FinalizeContext, Stage};
9+
use crate::parser::ArgParser;
10+
use crate::session_diagnostics;
11+
12+
pub(crate) struct MacroEscapeParser;
13+
impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
14+
const PATH: &[Symbol] = &[sym::macro_escape];
15+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
16+
const CREATE: fn(Span) -> AttributeKind = AttributeKind::MacroEscape;
17+
}
18+
19+
/// `#[macro_use]` attributes can either:
20+
/// - Use all macros from a crate, if provided without arguments
21+
/// - Use specific macros from a crate, if provided with arguments `#[macro_use(macro1, macro2)]`
22+
/// A warning should be provided if an use all is combined with specific uses, or if multiple use-alls are used.
23+
#[derive(Default)]
24+
pub(crate) struct MacroUseParser {
25+
state: MacroUseArgs,
26+
27+
/// Spans of all `#[macro_use]` arguments with arguments, used for linting
28+
uses_attr_spans: ThinVec<Span>,
29+
/// If `state` is `UseSpecific`, stores the span of the first `#[macro_use]` argument, used as the span for this attribute
30+
/// If `state` is `UseAll`, stores the span of the first `#[macro_use]` arguments without arguments
31+
first_span: Option<Span>,
32+
}
33+
34+
const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ...");
35+
36+
impl<S: Stage> AttributeParser<S> for MacroUseParser {
37+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
38+
&[sym::macro_use],
39+
MACRO_USE_TEMPLATE,
40+
|group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| {
41+
let span = cx.attr_span;
42+
group.first_span.get_or_insert(span);
43+
match args {
44+
ArgParser::NoArgs => {
45+
match group.state {
46+
MacroUseArgs::UseAll => {
47+
let first_span = group.first_span.expect(
48+
"State is UseAll is some so this is not the first attribute",
49+
);
50+
// Since there is a `#[macro_use]` import already, give a warning
51+
cx.warn_unused_duplicate(first_span, span);
52+
}
53+
MacroUseArgs::UseSpecific(_) => {
54+
group.state = MacroUseArgs::UseAll;
55+
group.first_span = Some(span);
56+
// If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
57+
for specific_use in group.uses_attr_spans.drain(..) {
58+
cx.warn_unused_duplicate(span, specific_use);
59+
}
60+
}
61+
}
62+
}
63+
ArgParser::List(list) => {
64+
if list.is_empty() {
65+
cx.warn_empty_attribute(list.span);
66+
return;
67+
}
68+
69+
match &mut group.state {
70+
MacroUseArgs::UseAll => {
71+
let first_span = group.first_span.expect(
72+
"State is UseAll is some so this is not the first attribute",
73+
);
74+
cx.warn_unused_duplicate(first_span, span);
75+
}
76+
MacroUseArgs::UseSpecific(arguments) => {
77+
// Store here so if we encounter a `UseAll` later we can still lint this attribute
78+
group.uses_attr_spans.push(cx.attr_span);
79+
80+
for item in list.mixed() {
81+
let Some(item) = item.meta_item() else {
82+
cx.expected_identifier(item.span());
83+
continue;
84+
};
85+
if let Err(err_span) = item.args().no_args() {
86+
cx.expected_no_args(err_span);
87+
continue;
88+
}
89+
let Some(item) = item.path().word() else {
90+
cx.expected_identifier(item.span());
91+
continue;
92+
};
93+
arguments.push(item);
94+
}
95+
}
96+
}
97+
}
98+
ArgParser::NameValue(_) => {
99+
let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
100+
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
101+
num_suggestions: suggestions.len(),
102+
suggestions: DiagArgValue::StrListSepByAnd(
103+
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
104+
),
105+
span,
106+
});
107+
}
108+
}
109+
},
110+
)];
111+
112+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
113+
Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
114+
}
115+
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub(crate) mod inline;
3636
pub(crate) mod link_attrs;
3737
pub(crate) mod lint_helpers;
3838
pub(crate) mod loop_match;
39+
pub(crate) mod macro_attrs;
3940
pub(crate) mod must_use;
4041
pub(crate) mod no_implicit_prelude;
4142
pub(crate) mod non_exhaustive;

compiler/rustc_attr_parsing/src/attributes/must_use.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
3434
ArgParser::List(_) => {
3535
let suggestions =
3636
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
37-
cx.emit_err(session_diagnostics::MustUseIllFormedAttributeInput {
37+
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
3838
num_suggestions: suggestions.len(),
3939
suggestions: DiagArgValue::StrListSepByAnd(
4040
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::attributes::lint_helpers::{
3333
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
3434
};
3535
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
36+
use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser};
3637
use crate::attributes::must_use::MustUseParser;
3738
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
3839
use crate::attributes::non_exhaustive::NonExhaustiveParser;
@@ -126,6 +127,7 @@ attribute_parsers!(
126127
BodyStabilityParser,
127128
ConfusablesParser,
128129
ConstStabilityParser,
130+
MacroUseParser,
129131
NakedParser,
130132
StabilityParser,
131133
UsedParser,
@@ -174,6 +176,7 @@ attribute_parsers!(
174176
Single<WithoutArgs<FfiPureParser>>,
175177
Single<WithoutArgs<FundamentalParser>>,
176178
Single<WithoutArgs<LoopMatchParser>>,
179+
Single<WithoutArgs<MacroEscapeParser>>,
177180
Single<WithoutArgs<MarkerParser>>,
178181
Single<WithoutArgs<MayDangleParser>>,
179182
Single<WithoutArgs<NoImplicitPreludeParser>>,
@@ -386,6 +389,17 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
386389
})
387390
}
388391

392+
/// emit an error that a `name` was expected here
393+
pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
394+
self.emit_err(AttributeParseError {
395+
span,
396+
attr_span: self.attr_span,
397+
template: self.template.clone(),
398+
attribute: self.attr_path.clone(),
399+
reason: AttributeParseErrorReason::ExpectedIdentifier,
400+
})
401+
}
402+
389403
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
390404
/// a nicer error message talking about the specific name that was found lacking a value.
391405
pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ pub(crate) struct IllFormedAttributeInput {
438438

439439
#[derive(Diagnostic)]
440440
#[diag(attr_parsing_ill_formed_attribute_input)]
441-
pub(crate) struct MustUseIllFormedAttributeInput {
441+
pub(crate) struct IllFormedAttributeInputLint {
442442
#[primary_span]
443443
pub span: Span,
444444
pub num_suggestions: usize,
@@ -549,6 +549,7 @@ pub(crate) enum AttributeParseErrorReason {
549549
/// Should we tell the user to write a list when they didn't?
550550
list: bool,
551551
},
552+
ExpectedIdentifier,
552553
}
553554

554555
pub(crate) struct AttributeParseError {
@@ -600,11 +601,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
600601
diag.code(E0538);
601602
}
602603
AttributeParseErrorReason::UnexpectedLiteral => {
603-
diag.span_label(self.span, format!("didn't expect a literal here"));
604+
diag.span_label(self.span, "didn't expect a literal here");
604605
diag.code(E0565);
605606
}
606607
AttributeParseErrorReason::ExpectedNoArgs => {
607-
diag.span_label(self.span, format!("didn't expect any arguments here"));
608+
diag.span_label(self.span, "didn't expect any arguments here");
608609
diag.code(E0565);
609610
}
610611
AttributeParseErrorReason::ExpectedNameValue(None) => {
@@ -684,6 +685,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
684685
}
685686
}
686687
}
688+
AttributeParseErrorReason::ExpectedIdentifier => {
689+
diag.span_label(self.span, "expected a valid identifier here");
690+
}
687691
}
688692

689693
let suggestions = self.template.suggestions(false, &name);

compiler/rustc_error_codes/src/error_codes/E0466.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
#### Note: this error code is no longer emitted by the compiler.
2+
13
Macro import declaration was malformed.
24

35
Erroneous code examples:
46

5-
```compile_fail,E0466
7+
```compile_fail
68
#[macro_use(a_macro(another_macro))] // error: invalid import declaration
79
extern crate core as some_crate;
810

compiler/rustc_hir/src/hir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,7 @@ impl AttributeExt for Attribute {
13021302
// FIXME: should not be needed anymore when all attrs are parsed
13031303
Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
13041304
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
1305+
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span,
13051306
Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
13061307
Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
13071308
Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span,

0 commit comments

Comments
 (0)