Skip to content

Commit 6c4ded0

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

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
@@ -141,6 +141,13 @@ pub enum UsedBy {
141141
Linker,
142142
}
143143

144+
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
145+
#[derive(HashStable_Generic, PrintAttribute)]
146+
pub enum MacroUseArgs {
147+
UseAll,
148+
UseSpecific(ThinVec<Ident>),
149+
}
150+
144151
#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
145152
pub struct StrippedCfgItem<ModId = DefId> {
146153
pub parent_module: ModId,
@@ -331,9 +338,15 @@ pub enum AttributeKind {
331338
/// Represents `#[loop_match]`.
332339
LoopMatch(Span),
333340

341+
/// Represents `#[macro_escape]`.
342+
MacroEscape(Span),
343+
334344
/// Represents `#[rustc_macro_transparency]`.
335345
MacroTransparency(Transparency),
336346

347+
/// Represents `#[macro_use]`.
348+
MacroUse { span: Span, arguments: MacroUseArgs },
349+
337350
/// Represents `#[marker]`.
338351
Marker(Span),
339352

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
@@ -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
@@ -30,6 +30,7 @@ use crate::attributes::lint_helpers::{
3030
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
3131
};
3232
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
33+
use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser};
3334
use crate::attributes::must_use::MustUseParser;
3435
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
3536
use crate::attributes::non_exhaustive::NonExhaustiveParser;
@@ -123,6 +124,7 @@ attribute_parsers!(
123124
BodyStabilityParser,
124125
ConfusablesParser,
125126
ConstStabilityParser,
127+
MacroUseParser,
126128
NakedParser,
127129
StabilityParser,
128130
UsedParser,
@@ -169,6 +171,7 @@ attribute_parsers!(
169171
Single<WithoutArgs<FfiPureParser>>,
170172
Single<WithoutArgs<FundamentalParser>>,
171173
Single<WithoutArgs<LoopMatchParser>>,
174+
Single<WithoutArgs<MacroEscapeParser>>,
172175
Single<WithoutArgs<MarkerParser>>,
173176
Single<WithoutArgs<MayDangleParser>>,
174177
Single<WithoutArgs<NoImplicitPreludeParser>>,
@@ -380,6 +383,17 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
380383
})
381384
}
382385

386+
/// emit an error that a `name` was expected here
387+
pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
388+
self.emit_err(AttributeParseError {
389+
span,
390+
attr_span: self.attr_span,
391+
template: self.template.clone(),
392+
attribute: self.attr_path.clone(),
393+
reason: AttributeParseErrorReason::ExpectedIdentifier,
394+
})
395+
}
396+
383397
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
384398
/// a nicer error message talking about the specific name that was found lacking a value.
385399
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) => {
@@ -633,6 +634,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
633634
}
634635
}
635636
}
637+
AttributeParseErrorReason::ExpectedIdentifier => {
638+
diag.span_label(self.span, "expected a valid identifier here");
639+
}
636640
}
637641

638642
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
@@ -300,6 +300,8 @@ pub fn check_builtin_meta_item(
300300
| sym::cold
301301
| sym::target_feature
302302
| sym::rustc_allow_const_fn_unstable
303+
| sym::macro_use
304+
| sym::macro_escape
303305
| sym::naked
304306
| sym::no_mangle
305307
| sym::non_exhaustive

compiler/rustc_passes/src/check_attr.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
214214
Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => {
215215
self.check_link_section(hir_id, *attr_span, span, target)
216216
}
217+
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => {
218+
self.check_macro_use(hir_id, sym::macro_use, *span, target)
219+
}
220+
Attribute::Parsed(AttributeKind::MacroEscape(span)) => {
221+
self.check_macro_use(hir_id, sym::macro_escape, *span, target)
222+
}
217223
Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
218224
self.check_naked(hir_id, *attr_span, span, target)
219225
}
@@ -343,9 +349,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
343349
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
344350
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
345351
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
346-
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
347-
self.check_macro_use(hir_id, attr, target)
348-
}
349352
[sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod),
350353
[sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
351354
[sym::should_panic, ..] => {
@@ -2314,17 +2317,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
23142317
}
23152318
}
23162319

2317-
fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2318-
let Some(name) = attr.name() else {
2319-
return;
2320-
};
2320+
fn check_macro_use(&self, hir_id: HirId, name: Symbol, attr_span: Span, target: Target) {
23212321
match target {
23222322
Target::ExternCrate | Target::Mod => {}
23232323
_ => {
23242324
self.tcx.emit_node_span_lint(
23252325
UNUSED_ATTRIBUTES,
23262326
hir_id,
2327-
attr.span(),
2327+
attr_span,
23282328
errors::MacroUse { name },
23292329
);
23302330
}
@@ -2377,7 +2377,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
23772377
// Warn on useless empty attributes.
23782378
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
23792379
let note = if attr.has_any_name(&[
2380-
sym::macro_use,
23812380
sym::allow,
23822381
sym::expect,
23832382
sym::warn,

0 commit comments

Comments
 (0)