Skip to content

Commit 887be8d

Browse files
Ports #[macro_use] and #[macro_escape] to the new attribute parsing infrastructure
Signed-off-by: Jonathan Brouwer <[email protected]>
1 parent c8bd6db commit 887be8d

File tree

14 files changed

+189
-64
lines changed

14 files changed

+189
-64
lines changed

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ 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+
160167
#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
161168
pub struct StrippedCfgItem<ModId = DefId> {
162169
pub parent_module: ModId,
@@ -350,9 +357,15 @@ pub enum AttributeKind {
350357
/// Represents `#[loop_match]`.
351358
LoopMatch(Span),
352359

360+
/// Represents `#[macro_escape]`.
361+
MacroEscape(Span),
362+
353363
/// Represents `#[rustc_macro_transparency]`.
354364
MacroTransparency(Transparency),
355365

366+
/// Represents `#[macro_use]`.
367+
MacroUse { span: Span, arguments: MacroUseArgs },
368+
356369
/// Represents `#[marker]`.
357370
Marker(Span),
358371

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 { .. } => No,
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: 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
@@ -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/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_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,

compiler/rustc_parse/src/validate_attr.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ pub fn check_builtin_meta_item(
301301
| sym::cold
302302
| sym::target_feature
303303
| sym::rustc_allow_const_fn_unstable
304+
| sym::macro_use
305+
| sym::macro_escape
304306
| sym::naked
305307
| sym::no_mangle
306308
| sym::non_exhaustive

compiler/rustc_passes/src/check_attr.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
227227
Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => {
228228
self.check_link_section(hir_id, *attr_span, span, target)
229229
}
230+
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => {
231+
self.check_macro_use(hir_id, sym::macro_use, *span, target)
232+
}
233+
Attribute::Parsed(AttributeKind::MacroEscape(span)) => {
234+
self.check_macro_use(hir_id, sym::macro_escape, *span, target)
235+
}
230236
Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
231237
self.check_naked(hir_id, *attr_span, span, target)
232238
}
@@ -362,9 +368,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
362368
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
363369
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
364370
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
365-
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
366-
self.check_macro_use(hir_id, attr, target)
367-
}
368371
[sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod),
369372
[sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
370373
[sym::should_panic, ..] => {
@@ -2410,17 +2413,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
24102413
}
24112414
}
24122415

2413-
fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2414-
let Some(name) = attr.name() else {
2415-
return;
2416-
};
2416+
fn check_macro_use(&self, hir_id: HirId, name: Symbol, attr_span: Span, target: Target) {
24172417
match target {
24182418
Target::ExternCrate | Target::Mod => {}
24192419
_ => {
24202420
self.tcx.emit_node_span_lint(
24212421
UNUSED_ATTRIBUTES,
24222422
hir_id,
2423-
attr.span(),
2423+
attr_span,
24242424
errors::MacroUse { name },
24252425
);
24262426
}
@@ -2473,7 +2473,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
24732473
// Warn on useless empty attributes.
24742474
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
24752475
let note = if attr.has_any_name(&[
2476-
sym::macro_use,
24772476
sym::allow,
24782477
sym::expect,
24792478
sym::warn,

0 commit comments

Comments
 (0)