Skip to main content

rustc_attr_parsing/attributes/
macro_attrs.rs

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