Skip to main content

rustc_attr_parsing/attributes/
macro_attrs.rs

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