Skip to main content

rustc_attr_parsing/attributes/
macro_attrs.rs

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