Skip to content

Commit 6a22cf7

Browse files
Ports #[macro_use] and #[macro_escape] to the new attribute parsing infrastructure
Signed-off-by: Jonathan Brouwer <jonathantbrouwer@gmail.com>
1 parent 20cec77 commit 6a22cf7

File tree

14 files changed

+189
-65
lines changed

14 files changed

+189
-65
lines changed

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_ast::token::CommentKind;
33
use rustc_ast::{self as ast, AttrStyle};
44
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
55
use rustc_span::hygiene::Transparency;
6-
use rustc_span::{Span, Symbol};
6+
use rustc_span::{Ident, Span, Symbol};
77
use thin_vec::ThinVec;
88

99
use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
@@ -140,6 +140,13 @@ pub enum UsedBy {
140140
Linker,
141141
}
142142

143+
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
144+
#[derive(HashStable_Generic, PrintAttribute)]
145+
pub enum MacroUseArgs {
146+
UseAll,
147+
UseSpecific(ThinVec<Ident>),
148+
}
149+
143150
/// Represents parsed *built-in* inert attributes.
144151
///
145152
/// ## Overview
@@ -300,9 +307,15 @@ pub enum AttributeKind {
300307
/// Represents `#[loop_match]`.
301308
LoopMatch(Span),
302309

310+
/// Represents `#[macro_escape]`.
311+
MacroEscape(Span),
312+
303313
/// Represents `#[rustc_macro_transparency]`.
304314
MacroTransparency(Transparency),
305315

316+
/// Represents `#[macro_use]`.
317+
MacroUse { span: Span, arguments: MacroUseArgs },
318+
306319
/// Represents `#[marker]`.
307320
Marker(Span),
308321

compiler/rustc_attr_data_structures/src/encode_cross_crate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ impl AttributeKind {
4242
LinkName { .. } => Yes,
4343
LinkSection { .. } => No,
4444
LoopMatch(..) => No,
45+
MacroEscape(..) => No,
4546
MacroTransparency(..) => Yes,
47+
MacroUse { .. } => No,
4648
Marker(..) => No,
4749
MayDangle(..) => No,
4850
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::{Span, Symbol};
27+
use rustc_span::{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, ());
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: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use rustc_attr_data_structures::{AttributeKind, MacroUseArgs};
2+
use rustc_errors::DiagArgValue;
3+
use rustc_feature::{AttributeTemplate, template};
4+
use rustc_span::{Ident, 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+
/// All specific imports found so far
26+
uses: ThinVec<Ident>,
27+
/// Span of the first `#[macro_use]` arguments without arguments, used for linting
28+
use_all: Option<Span>,
29+
/// Spans of all `#[macro_use]` arguments with arguments, used for linting
30+
uses_attr_spans: ThinVec<Span>,
31+
/// Span of the first `#[macro_use]` argument, used as the span for this attribute
32+
first_span: Option<Span>,
33+
}
34+
35+
const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ...");
36+
37+
impl<S: Stage> AttributeParser<S> for MacroUseParser {
38+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
39+
&[sym::macro_use],
40+
MACRO_USE_TEMPLATE,
41+
|group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| {
42+
let span = cx.attr_span;
43+
group.first_span.get_or_insert(span);
44+
match args {
45+
ArgParser::NoArgs => {
46+
// If there is a `#[macro_use]` import already, give a warning
47+
if let Some(old_attr) = group.use_all.replace(span) {
48+
cx.warn_unused_duplicate(old_attr, span);
49+
}
50+
}
51+
ArgParser::List(list) => {
52+
let mut arguments = ThinVec::new();
53+
54+
if list.is_empty() {
55+
cx.warn_empty_attribute(list.span);
56+
return;
57+
}
58+
group.uses_attr_spans.push(cx.attr_span);
59+
60+
for item in list.mixed() {
61+
let Some(item) = item.meta_item() else {
62+
cx.expected_identifier(item.span());
63+
continue;
64+
};
65+
if let Err(err_span) = item.args().no_args() {
66+
cx.expected_no_args(err_span);
67+
continue;
68+
}
69+
let Some(item) = item.path().word() else {
70+
cx.expected_identifier(item.span());
71+
continue;
72+
};
73+
arguments.push(item);
74+
}
75+
76+
group.uses.extend(arguments);
77+
}
78+
ArgParser::NameValue(_) => {
79+
let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
80+
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
81+
num_suggestions: suggestions.len(),
82+
suggestions: DiagArgValue::StrListSepByAnd(
83+
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
84+
),
85+
span,
86+
});
87+
return;
88+
}
89+
};
90+
},
91+
)];
92+
93+
fn finalize(self, cx: &mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
94+
let arguments = if let Some(use_all) = self.use_all {
95+
// If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
96+
for specific_use in self.uses_attr_spans {
97+
cx.warn_unused_duplicate(use_all, specific_use);
98+
}
99+
MacroUseArgs::UseAll
100+
} else {
101+
MacroUseArgs::UseSpecific(self.uses)
102+
};
103+
Some(AttributeKind::MacroUse { span: self.first_span?, arguments })
104+
}
105+
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

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

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::attributes::link_attrs::{
2929
};
3030
use crate::attributes::lint_helpers::{AsPtrParser, PassByValueParser, PubTransparentParser};
3131
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
32+
use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser};
3233
use crate::attributes::must_use::MustUseParser;
3334
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
3435
use crate::attributes::non_exhaustive::NonExhaustiveParser;
@@ -122,6 +123,7 @@ attribute_parsers!(
122123
BodyStabilityParser,
123124
ConfusablesParser,
124125
ConstStabilityParser,
126+
MacroUseParser,
125127
NakedParser,
126128
StabilityParser,
127129
UsedParser,
@@ -166,6 +168,7 @@ attribute_parsers!(
166168
Single<WithoutArgs<FfiPureParser>>,
167169
Single<WithoutArgs<FundamentalParser>>,
168170
Single<WithoutArgs<LoopMatchParser>>,
171+
Single<WithoutArgs<MacroEscapeParser>>,
169172
Single<WithoutArgs<MarkerParser>>,
170173
Single<WithoutArgs<MayDangleParser>>,
171174
Single<WithoutArgs<NoImplicitPreludeParser>>,
@@ -355,6 +358,17 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
355358
})
356359
}
357360

361+
/// emit an error that a `name` was expected here
362+
pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
363+
self.emit_err(AttributeParseError {
364+
span,
365+
attr_span: self.attr_span,
366+
template: self.template.clone(),
367+
attribute: self.attr_path.clone(),
368+
reason: AttributeParseErrorReason::ExpectedIdentifier,
369+
})
370+
}
371+
358372
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
359373
/// a nicer error message talking about the specific name that was found lacking a value.
360374
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,
@@ -525,6 +525,7 @@ pub(crate) enum AttributeParseErrorReason {
525525
ExpectedNameValue(Option<Symbol>),
526526
DuplicateKey(Symbol),
527527
ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
528+
ExpectedIdentifier,
528529
}
529530

530531
pub(crate) struct AttributeParseError {
@@ -576,11 +577,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
576577
diag.code(E0538);
577578
}
578579
AttributeParseErrorReason::UnexpectedLiteral => {
579-
diag.span_label(self.span, format!("didn't expect a literal here"));
580+
diag.span_label(self.span, "didn't expect a literal here");
580581
diag.code(E0565);
581582
}
582583
AttributeParseErrorReason::ExpectedNoArgs => {
583-
diag.span_label(self.span, format!("didn't expect any arguments here"));
584+
diag.span_label(self.span, "didn't expect any arguments here");
584585
diag.code(E0565);
585586
}
586587
AttributeParseErrorReason::ExpectedNameValue(None) => {
@@ -618,6 +619,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
618619
}
619620
}
620621
}
622+
AttributeParseErrorReason::ExpectedIdentifier => {
623+
diag.span_label(self.span, "expected a valid identifier here");
624+
}
621625
}
622626

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

compiler/rustc_hir/src/hir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,7 @@ impl AttributeExt for Attribute {
13041304
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
13051305
Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
13061306
Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
1307+
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span,
13071308
a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
13081309
}
13091310
}

compiler/rustc_parse/src/validate_attr.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ pub fn check_builtin_meta_item(
316316
| sym::rustc_layout_scalar_valid_range_start
317317
| sym::rustc_layout_scalar_valid_range_end
318318
| sym::no_implicit_prelude
319+
| sym::macro_use
320+
| sym::macro_escape
319321
) {
320322
return;
321323
}

compiler/rustc_passes/src/check_attr.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
205205
Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => {
206206
self.check_link_section(hir_id, *attr_span, span, target)
207207
}
208+
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => {
209+
self.check_macro_use(hir_id, sym::macro_use, *span, target)
210+
}
211+
Attribute::Parsed(AttributeKind::MacroEscape(span)) => {
212+
self.check_macro_use(hir_id, sym::macro_escape, *span, target)
213+
}
208214
Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
209215
self.check_naked(hir_id, *attr_span, span, target)
210216
}
@@ -329,9 +335,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
329335
}
330336
[sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
331337
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
332-
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
333-
self.check_macro_use(hir_id, attr, target)
334-
}
335338
[sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod),
336339
[sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
337340
[sym::should_panic, ..] => {
@@ -2286,17 +2289,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
22862289
}
22872290
}
22882291

2289-
fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2290-
let Some(name) = attr.name() else {
2291-
return;
2292-
};
2292+
fn check_macro_use(&self, hir_id: HirId, name: Symbol, attr_span: Span, target: Target) {
22932293
match target {
22942294
Target::ExternCrate | Target::Mod => {}
22952295
_ => {
22962296
self.tcx.emit_node_span_lint(
22972297
UNUSED_ATTRIBUTES,
22982298
hir_id,
2299-
attr.span(),
2299+
attr_span,
23002300
errors::MacroUse { name },
23012301
);
23022302
}
@@ -2349,7 +2349,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
23492349
// Warn on useless empty attributes.
23502350
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
23512351
let note = if attr.has_any_name(&[
2352-
sym::macro_use,
23532352
sym::allow,
23542353
sym::expect,
23552354
sym::warn,

0 commit comments

Comments
 (0)