rustc_attr_parsing/attributes/
macro_attrs.rs

1use rustc_ast::AttrStyle;
2use rustc_errors::DiagArgValue;
3use rustc_hir::attrs::MacroUseArgs;
4
5use super::prelude::*;
6use crate::session_diagnostics::IllFormedAttributeInputLint;
7
8pub(crate) struct MacroEscapeParser;
9impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
10    const PATH: &[Symbol] = &[sym::macro_escape];
11    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
12    const ALLOWED_TARGETS: AllowedTargets = MACRO_USE_ALLOWED_TARGETS;
13    const CREATE: fn(Span) -> AttributeKind = AttributeKind::MacroEscape;
14}
15
16/// `#[macro_use]` attributes can either:
17/// - Use all macros from a crate, if provided without arguments
18/// - Use specific macros from a crate, if provided with arguments `#[macro_use(macro1, macro2)]`
19/// A warning should be provided if an use all is combined with specific uses, or if multiple use-alls are used.
20#[derive(Default)]
21pub(crate) struct MacroUseParser {
22    state: MacroUseArgs,
23
24    /// Spans of all `#[macro_use]` arguments with arguments, used for linting
25    uses_attr_spans: ThinVec<Span>,
26    /// If `state` is `UseSpecific`, stores the span of the first `#[macro_use]` argument, used as the span for this attribute
27    /// If `state` is `UseAll`, stores the span of the first `#[macro_use]` arguments without arguments
28    first_span: Option<Span>,
29}
30
31const MACRO_USE_TEMPLATE: AttributeTemplate = template!(
32    Word, List: &["name1, name2, ..."],
33    "https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute"
34);
35const MACRO_USE_ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
36    Allow(Target::Mod),
37    Allow(Target::ExternCrate),
38    Allow(Target::Crate),
39    Error(Target::WherePredicate),
40]);
41
42impl<S: Stage> AttributeParser<S> for MacroUseParser {
43    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
44        &[sym::macro_use],
45        MACRO_USE_TEMPLATE,
46        |group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| {
47            let span = cx.attr_span;
48            group.first_span.get_or_insert(span);
49            match args {
50                ArgParser::NoArgs => {
51                    match group.state {
52                        MacroUseArgs::UseAll => {
53                            let first_span = group.first_span.expect(
54                                "State is UseAll is some so this is not the first attribute",
55                            );
56                            // Since there is a `#[macro_use]` import already, give a warning
57                            cx.warn_unused_duplicate(first_span, span);
58                        }
59                        MacroUseArgs::UseSpecific(_) => {
60                            group.state = MacroUseArgs::UseAll;
61                            group.first_span = Some(span);
62                            // If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
63                            for specific_use in group.uses_attr_spans.drain(..) {
64                                cx.warn_unused_duplicate(span, specific_use);
65                            }
66                        }
67                    }
68                }
69                ArgParser::List(list) => {
70                    if list.is_empty() {
71                        cx.warn_empty_attribute(list.span);
72                        return;
73                    }
74
75                    match &mut group.state {
76                        MacroUseArgs::UseAll => {
77                            let first_span = group.first_span.expect(
78                                "State is UseAll is some so this is not the first attribute",
79                            );
80                            cx.warn_unused_duplicate(first_span, span);
81                        }
82                        MacroUseArgs::UseSpecific(arguments) => {
83                            // Store here so if we encounter a `UseAll` later we can still lint this attribute
84                            group.uses_attr_spans.push(cx.attr_span);
85
86                            for item in list.mixed() {
87                                let Some(item) = item.meta_item() else {
88                                    cx.expected_identifier(item.span());
89                                    continue;
90                                };
91                                if let Err(err_span) = item.args().no_args() {
92                                    cx.expected_no_args(err_span);
93                                    continue;
94                                }
95                                let Some(item) = item.path().word() else {
96                                    cx.expected_identifier(item.span());
97                                    continue;
98                                };
99                                arguments.push(item);
100                            }
101                        }
102                    }
103                }
104                ArgParser::NameValue(_) => {
105                    let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
106                    cx.emit_err(IllFormedAttributeInputLint {
107                        num_suggestions: suggestions.len(),
108                        suggestions: DiagArgValue::StrListSepByAnd(
109                            suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
110                        ),
111                        span,
112                    });
113                }
114            }
115        },
116    )];
117    const ALLOWED_TARGETS: AllowedTargets = MACRO_USE_ALLOWED_TARGETS;
118
119    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
120        Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
121    }
122}
123
124pub(crate) struct AllowInternalUnsafeParser;
125
126impl<S: Stage> NoArgsAttributeParser<S> for AllowInternalUnsafeParser {
127    const PATH: &[Symbol] = &[sym::allow_internal_unsafe];
128    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
129    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
130        Allow(Target::Fn),
131        Allow(Target::MacroDef),
132        Warn(Target::Field),
133        Warn(Target::Arm),
134    ]);
135    const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span);
136}
137
138pub(crate) struct MacroExportParser;
139
140impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
141    const PATH: &[Symbol] = &[sym::macro_export];
142    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
143    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
144    const TEMPLATE: AttributeTemplate = template!(Word, List: &["local_inner_macros"]);
145    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
146        Allow(Target::MacroDef),
147        Error(Target::WherePredicate),
148        Error(Target::Crate),
149    ]);
150
151    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
152        let suggestions = || {
153            <Self as SingleAttributeParser<S>>::TEMPLATE
154                .suggestions(AttrStyle::Inner, "macro_export")
155        };
156        let local_inner_macros = match args {
157            ArgParser::NoArgs => false,
158            ArgParser::List(list) => {
159                let Some(l) = list.single() else {
160                    let span = cx.attr_span;
161                    cx.emit_lint(
162                        AttributeLintKind::InvalidMacroExportArguments {
163                            suggestions: suggestions(),
164                        },
165                        span,
166                    );
167                    return None;
168                };
169                match l.meta_item().and_then(|i| i.path().word_sym()) {
170                    Some(sym::local_inner_macros) => true,
171                    _ => {
172                        let span = cx.attr_span;
173                        cx.emit_lint(
174                            AttributeLintKind::InvalidMacroExportArguments {
175                                suggestions: suggestions(),
176                            },
177                            span,
178                        );
179                        return None;
180                    }
181                }
182            }
183            ArgParser::NameValue(_) => {
184                let span = cx.attr_span;
185                let suggestions = suggestions();
186                cx.emit_err(IllFormedAttributeInputLint {
187                    num_suggestions: suggestions.len(),
188                    suggestions: DiagArgValue::StrListSepByAnd(
189                        suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
190                    ),
191                    span,
192                });
193                return None;
194            }
195        };
196        Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros })
197    }
198}