rustc_attr_parsing/attributes/
macro_attrs.rs

1use rustc_errors::DiagArgValue;
2use rustc_feature::{AttributeTemplate, template};
3use rustc_hir::attrs::{AttributeKind, MacroUseArgs};
4use rustc_span::{Span, Symbol, sym};
5use thin_vec::ThinVec;
6
7use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate};
8use crate::context::{AcceptContext, FinalizeContext, Stage};
9use crate::parser::ArgParser;
10use crate::session_diagnostics;
11
12pub(crate) struct MacroEscapeParser;
13impl<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)]
24pub(crate) struct MacroUseParser {
25    state: MacroUseArgs,
26
27    /// Spans of all `#[macro_use]` arguments with arguments, used for linting
28    uses_attr_spans: ThinVec<Span>,
29    /// If `state` is `UseSpecific`, stores the span of the first `#[macro_use]` argument, used as the span for this attribute
30    /// If `state` is `UseAll`, stores the span of the first `#[macro_use]` arguments without arguments
31    first_span: Option<Span>,
32}
33
34const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ...");
35
36impl<S: Stage> AttributeParser<S> for MacroUseParser {
37    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
38        &[sym::macro_use],
39        MACRO_USE_TEMPLATE,
40        |group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| {
41            let span = cx.attr_span;
42            group.first_span.get_or_insert(span);
43            match args {
44                ArgParser::NoArgs => {
45                    match group.state {
46                        MacroUseArgs::UseAll => {
47                            let first_span = group.first_span.expect(
48                                "State is UseAll is some so this is not the first attribute",
49                            );
50                            // Since there is a `#[macro_use]` import already, give a warning
51                            cx.warn_unused_duplicate(first_span, span);
52                        }
53                        MacroUseArgs::UseSpecific(_) => {
54                            group.state = MacroUseArgs::UseAll;
55                            group.first_span = Some(span);
56                            // If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
57                            for specific_use in group.uses_attr_spans.drain(..) {
58                                cx.warn_unused_duplicate(span, specific_use);
59                            }
60                        }
61                    }
62                }
63                ArgParser::List(list) => {
64                    if list.is_empty() {
65                        cx.warn_empty_attribute(list.span);
66                        return;
67                    }
68
69                    match &mut group.state {
70                        MacroUseArgs::UseAll => {
71                            let first_span = group.first_span.expect(
72                                "State is UseAll is some so this is not the first attribute",
73                            );
74                            cx.warn_unused_duplicate(first_span, span);
75                        }
76                        MacroUseArgs::UseSpecific(arguments) => {
77                            // Store here so if we encounter a `UseAll` later we can still lint this attribute
78                            group.uses_attr_spans.push(cx.attr_span);
79
80                            for item in list.mixed() {
81                                let Some(item) = item.meta_item() else {
82                                    cx.expected_identifier(item.span());
83                                    continue;
84                                };
85                                if let Err(err_span) = item.args().no_args() {
86                                    cx.expected_no_args(err_span);
87                                    continue;
88                                }
89                                let Some(item) = item.path().word() else {
90                                    cx.expected_identifier(item.span());
91                                    continue;
92                                };
93                                arguments.push(item);
94                            }
95                        }
96                    }
97                }
98                ArgParser::NameValue(_) => {
99                    let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
100                    cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
101                        num_suggestions: suggestions.len(),
102                        suggestions: DiagArgValue::StrListSepByAnd(
103                            suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
104                        ),
105                        span,
106                    });
107                }
108            }
109        },
110    )];
111
112    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
113        Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
114    }
115}