Skip to main content

rustc_attr_parsing/attributes/
macro_attrs.rs

1use rustc_ast::ItemKind;
2use rustc_attr_ir::{MacroUseArgs, find_attr};
3use rustc_feature::AttributeStability;
4use rustc_lint_defs::builtin::{INVALID_MACRO_EXPORT_ARGUMENTS, UNUSED_ATTRIBUTES};
5use rustc_structures::CollapseMacroDebuginfo;
6
7use super::prelude::*;
8use crate::diagnostics::{MacroExport, MacroOnlyAttribute};
9
10pub(crate) struct MacroEscapeParser;
11impl NoArgsAttributeParser for MacroEscapeParser {
12    const PATH: &[Symbol] = &[sym::macro_escape];
13    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
14    const ALLOWED_TARGETS: AllowedTargets<'_> = MACRO_USE_ALLOWED_TARGETS;
15    const STABILITY: AttributeStability = AttributeStability::Stable;
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(#[automatically_derived]
impl ::core::default::Default for MacroUseParser {
    #[inline]
    fn default() -> Self {
        Self {
            state: ::core::default::Default::default(),
            uses_attr_spans: ::core::default::Default::default(),
            first_span: ::core::default::Default::default(),
        }
    }
}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 = crate::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!(
35    Word, List: &["name1, name2, ..."],
36    "https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute"
37);
38const MACRO_USE_ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
39    Allow(Target::Mod),
40    Allow(Target::ExternCrate),
41    Error(Target::WherePredicate),
42]);
43
44impl AttributeParser for MacroUseParser {
45    const ATTRIBUTES: AcceptMapping<Self> = &[(
46        &[sym::macro_use],
47        MACRO_USE_TEMPLATE,
48        AttributeStability::Stable,
49        |group: &mut Self, cx: &mut AcceptContext<'_, '_>, args| {
50            let span = cx.attr_span;
51            group.first_span.get_or_insert(span);
52            match args {
53                ArgParser::NoArgs => {
54                    match group.state {
55                        MacroUseArgs::UseAll => {
56                            let first_span = group.first_span.expect(
57                                "State is UseAll is some so this is not the first attribute",
58                            );
59                            // Since there is a `#[macro_use]` import already, give a warning
60                            cx.warn_unused_duplicate(first_span, span);
61                        }
62                        MacroUseArgs::UseSpecific(_) => {
63                            group.state = MacroUseArgs::UseAll;
64                            group.first_span = Some(span);
65                            // If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
66                            for specific_use in group.uses_attr_spans.drain(..) {
67                                cx.warn_unused_duplicate(span, specific_use);
68                            }
69                        }
70                    }
71                }
72                ArgParser::List(list) => {
73                    if list.is_empty() {
74                        cx.adcx().warn_empty_attribute(list.span);
75                        return;
76                    }
77
78                    match &mut group.state {
79                        MacroUseArgs::UseAll => {
80                            let first_span = group.first_span.expect(
81                                "State is UseAll is some so this is not the first attribute",
82                            );
83                            cx.warn_unused_duplicate(first_span, span);
84                        }
85                        MacroUseArgs::UseSpecific(arguments) => {
86                            // Store here so if we encounter a `UseAll` later we can still lint this attribute
87                            group.uses_attr_spans.push(cx.attr_span);
88
89                            for item in list.mixed() {
90                                let Some(item) = item.meta_item() else {
91                                    cx.adcx().expected_identifier(item.span());
92                                    continue;
93                                };
94                                let Some(()) = cx.expect_no_args(item.args()) else {
95                                    continue;
96                                };
97                                let Some(item) = item.path().word() else {
98                                    cx.adcx().expected_identifier(item.span());
99                                    continue;
100                                };
101                                arguments.push(item);
102                            }
103                        }
104                    }
105                }
106                ArgParser::NameValue(nv) => {
107                    cx.adcx().expected_list_or_no_args(nv.args_span());
108                }
109            }
110        },
111    )];
112    const ALLOWED_TARGETS: AllowedTargets<'_> = MACRO_USE_ALLOWED_TARGETS;
113
114    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
115        Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
116    }
117}
118
119/// `#[allow_internal_unsafe]` and `#[allow_internal_unstable]` may only be applied to macros.
120/// Applying them to a function is only allowed if that function is a procedural macro, i.e. it
121/// also carries `#[proc_macro]`, `#[proc_macro_attribute]`, or `#[proc_macro_derive]`.
122pub(crate) fn check_macro_only(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) {
123    if cx.target == Target::Fn
124        && !{
    {
            'done:
                {
                for i in cx.parsed_attrs {
                    #[allow(unused_imports)]
                    use ::rustc_attr_ir::AttributeKind::*;
                    let i: &::rustc_attr_ir::Attribute = i;
                    match i {
                        ::rustc_attr_ir::Attribute::Parsed(ProcMacro |
                            ProcMacroAttribute | ProcMacroDerive { .. }) => {
                            break 'done Some(());
                        }
                        ::rustc_attr_ir::Attribute::Unparsed(..) =>
                            {}
                            #[deny(unreachable_patterns)]
                            _ => {}
                    }
                }
                None
            }
        }.is_some()
}find_attr!(cx.parsed_attrs, ProcMacro | ProcMacroAttribute | ProcMacroDerive { .. })
125    {
126        cx.emit_err(MacroOnlyAttribute { attr_span, span: cx.target_span });
127    }
128}
129
130pub(crate) struct AllowInternalUnsafeParser;
131
132impl NoArgsAttributeParser for AllowInternalUnsafeParser {
133    const PATH: &[Symbol] = &[sym::allow_internal_unsafe];
134    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore;
135    const ALLOWED_TARGETS: AllowedTargets<'_> =
136        AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::MacroDef)]);
137    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::allow_internal_unsafe;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::allow_internal_unsafe,
        notes: &[],
    }
}unstable!(allow_internal_unsafe);
138    const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span);
139
140    fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) {
141        check_macro_only(cx, attr_span);
142    }
143}
144
145pub(crate) struct MacroExportParser;
146
147impl SingleAttributeParser for MacroExportParser {
148    const PATH: &[Symbol] = &[sym::macro_export];
149    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
150    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: true,
    list: Some(&["local_inner_macros"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word, List: &["local_inner_macros"]);
151    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
152        Allow(Target::MacroDef),
153        Error(Target::WherePredicate),
154        Error(Target::Crate),
155    ]);
156    const STABILITY: AttributeStability = AttributeStability::Stable;
157
158    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
159        let local_inner_macros = match args {
160            ArgParser::NoArgs => false,
161            ArgParser::List(list) => {
162                let Some(l) = list.as_single() else {
163                    cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
164                    return None;
165                };
166                if l.meta_item_no_args().is_some_and(|m| m.path().word_is(sym::local_inner_macros))
167                {
168                    true
169                } else {
170                    cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
171                    return None;
172                }
173            }
174            ArgParser::NameValue(nv) => {
175                cx.adcx().expected_list_or_no_args(nv.args_span());
176                return None;
177            }
178        };
179
180        Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros })
181    }
182
183    fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) {
184        if cx.target != Target::MacroDef {
185            return;
186        }
187
188        let item = cx.target_item.unwrap();
189        if let ItemKind::MacroDef(_, macro_def) = &item.kind
190            && !macro_def.macro_rules
191        {
192            cx.emit_lint(UNUSED_ATTRIBUTES, MacroExport::OnDeclMacro, attr_span);
193        }
194    }
195}
196
197pub(crate) struct CollapseDebugInfoParser;
198
199impl SingleAttributeParser for CollapseDebugInfoParser {
200    const PATH: &[Symbol] = &[sym::collapse_debuginfo];
201    const TEMPLATE: AttributeTemplate = crate::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!(
202        List: &["no", "external", "yes"],
203        "https://doc.rust-lang.org/reference/attributes/debugger.html#the-collapse_debuginfo-attribute"
204    );
205    const ALLOWED_TARGETS: AllowedTargets<'_> =
206        AllowedTargets::AllowList(&[Allow(Target::MacroDef)]);
207    const STABILITY: AttributeStability = AttributeStability::Stable;
208
209    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
210        let single = cx.expect_single_element_list(args, cx.attr_span)?;
211        let Some(mi) = single.meta_item() else {
212            cx.adcx().expected_not_literal(single.span());
213            return None;
214        };
215        let _ = cx.expect_no_args(mi.args());
216        let path = mi.path().word_sym();
217        let info = match path {
218            Some(sym::yes) => CollapseMacroDebuginfo::Yes,
219            Some(sym::no) => CollapseMacroDebuginfo::No,
220            Some(sym::external) => CollapseMacroDebuginfo::External,
221            _ => {
222                cx.adcx()
223                    .expected_specific_argument(mi.span(), &[sym::yes, sym::no, sym::external]);
224                return None;
225            }
226        };
227
228        Some(AttributeKind::CollapseDebugInfo(info))
229    }
230}
231
232pub(crate) struct RustcProcMacroDeclsParser;
233
234impl NoArgsAttributeParser for RustcProcMacroDeclsParser {
235    const PATH: &[Symbol] = &[sym::rustc_proc_macro_decls];
236    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Static)]);
237    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::rustc_attrs;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::rustc_attrs,
        notes: &[],
    }
}unstable!(rustc_attrs);
238    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcProcMacroDecls;
239}