Skip to main content

rustc_attr_parsing/attributes/
macro_attrs.rs

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