rustc_attr_parsing/attributes/
macro_attrs.rs

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