Skip to content

Commit a71aef6

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

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
@@ -306,9 +313,15 @@ pub enum AttributeKind {
306313
/// Represents `#[loop_match]`.
307314
LoopMatch(Span),
308315

316+
/// Represents `#[macro_escape]`.
317+
MacroEscape(Span),
318+
309319
/// Represents `#[rustc_macro_transparency]`.
310320
MacroTransparency(Transparency),
311321

322+
/// Represents `#[macro_use]`.
323+
MacroUse { span: Span, arguments: MacroUseArgs },
324+
312325
/// Represents `#[marker]`.
313326
Marker(Span),
314327

compiler/rustc_attr_data_structures/src/encode_cross_crate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ impl AttributeKind {
4444
LinkOrdinal { .. } => No,
4545
LinkSection { .. } => No,
4646
LoopMatch(..) => No,
47+
MacroEscape(..) => No,
4748
MacroTransparency(..) => Yes,
49+
MacroUse { .. } => No,
4850
Marker(..) => No,
4951
MayDangle(..) => No,
5052
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
@@ -31,6 +31,7 @@ use crate::attributes::lint_helpers::{
3131
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
3232
};
3333
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
34+
use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser};
3435
use crate::attributes::must_use::MustUseParser;
3536
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
3637
use crate::attributes::non_exhaustive::NonExhaustiveParser;
@@ -124,6 +125,7 @@ attribute_parsers!(
124125
BodyStabilityParser,
125126
ConfusablesParser,
126127
ConstStabilityParser,
128+
MacroUseParser,
127129
NakedParser,
128130
StabilityParser,
129131
UsedParser,
@@ -170,6 +172,7 @@ attribute_parsers!(
170172
Single<WithoutArgs<FfiPureParser>>,
171173
Single<WithoutArgs<FundamentalParser>>,
172174
Single<WithoutArgs<LoopMatchParser>>,
175+
Single<WithoutArgs<MacroEscapeParser>>,
173176
Single<WithoutArgs<MarkerParser>>,
174177
Single<WithoutArgs<MayDangleParser>>,
175178
Single<WithoutArgs<NoImplicitPreludeParser>>,
@@ -359,6 +362,17 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
359362
})
360363
}
361364

365+
/// emit an error that a `name` was expected here
366+
pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
367+
self.emit_err(AttributeParseError {
368+
span,
369+
attr_span: self.attr_span,
370+
template: self.template.clone(),
371+
attribute: self.attr_path.clone(),
372+
reason: AttributeParseErrorReason::ExpectedIdentifier,
373+
})
374+
}
375+
362376
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
363377
/// a nicer error message talking about the specific name that was found lacking a value.
364378
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,
@@ -534,6 +534,7 @@ pub(crate) enum AttributeParseErrorReason {
534534
ExpectedNameValue(Option<Symbol>),
535535
DuplicateKey(Symbol),
536536
ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
537+
ExpectedIdentifier,
537538
}
538539

539540
pub(crate) struct AttributeParseError {
@@ -585,11 +586,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
585586
diag.code(E0538);
586587
}
587588
AttributeParseErrorReason::UnexpectedLiteral => {
588-
diag.span_label(self.span, format!("didn't expect a literal here"));
589+
diag.span_label(self.span, "didn't expect a literal here");
589590
diag.code(E0565);
590591
}
591592
AttributeParseErrorReason::ExpectedNoArgs => {
592-
diag.span_label(self.span, format!("didn't expect any arguments here"));
593+
diag.span_label(self.span, "didn't expect any arguments here");
593594
diag.code(E0565);
594595
}
595596
AttributeParseErrorReason::ExpectedNameValue(None) => {
@@ -627,6 +628,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
627628
}
628629
}
629630
}
631+
AttributeParseErrorReason::ExpectedIdentifier => {
632+
diag.span_label(self.span, "expected a valid identifier here");
633+
}
630634
}
631635

632636
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
@@ -302,6 +302,8 @@ pub fn check_builtin_meta_item(
302302
| sym::cold
303303
| sym::target_feature
304304
| sym::rustc_allow_const_fn_unstable
305+
| sym::macro_use
306+
| sym::macro_escape
305307
| sym::naked
306308
| sym::no_mangle
307309
| sym::non_exhaustive

compiler/rustc_passes/src/check_attr.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
213213
Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => {
214214
self.check_link_section(hir_id, *attr_span, span, target)
215215
}
216+
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => {
217+
self.check_macro_use(hir_id, sym::macro_use, *span, target)
218+
}
219+
Attribute::Parsed(AttributeKind::MacroEscape(span)) => {
220+
self.check_macro_use(hir_id, sym::macro_escape, *span, target)
221+
}
216222
Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
217223
self.check_naked(hir_id, *attr_span, span, target)
218224
}
@@ -341,9 +347,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
341347
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
342348
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
343349
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
344-
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
345-
self.check_macro_use(hir_id, attr, target)
346-
}
347350
[sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod),
348351
[sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
349352
[sym::should_panic, ..] => {
@@ -2298,17 +2301,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
22982301
}
22992302
}
23002303

2301-
fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2302-
let Some(name) = attr.name() else {
2303-
return;
2304-
};
2304+
fn check_macro_use(&self, hir_id: HirId, name: Symbol, attr_span: Span, target: Target) {
23052305
match target {
23062306
Target::ExternCrate | Target::Mod => {}
23072307
_ => {
23082308
self.tcx.emit_node_span_lint(
23092309
UNUSED_ATTRIBUTES,
23102310
hir_id,
2311-
attr.span(),
2311+
attr_span,
23122312
errors::MacroUse { name },
23132313
);
23142314
}
@@ -2361,7 +2361,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
23612361
// Warn on useless empty attributes.
23622362
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
23632363
let note = if attr.has_any_name(&[
2364-
sym::macro_use,
23652364
sym::allow,
23662365
sym::expect,
23672366
sym::warn,

0 commit comments

Comments
 (0)