about summary refs log tree commit diff
path: root/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
blob: 8928129c2013d9ead1b7c1e864dd42682885c076 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use rustc_errors::DiagArgValue;
use rustc_feature::{AttributeTemplate, template};
use rustc_hir::Target;
use rustc_hir::attrs::{AttributeKind, MacroUseArgs};
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;

use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate};
use crate::context::MaybeWarn::{Allow, Error, Warn};
use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics;
pub(crate) struct MacroEscapeParser;
impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
    const PATH: &[Symbol] = &[sym::macro_escape];
    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
    const ALLOWED_TARGETS: AllowedTargets = MACRO_USE_ALLOWED_TARGETS;
    const CREATE: fn(Span) -> AttributeKind = AttributeKind::MacroEscape;
}

/// `#[macro_use]` attributes can either:
/// - Use all macros from a crate, if provided without arguments
/// - Use specific macros from a crate, if provided with arguments `#[macro_use(macro1, macro2)]`
/// A warning should be provided if an use all is combined with specific uses, or if multiple use-alls are used.
#[derive(Default)]
pub(crate) struct MacroUseParser {
    state: MacroUseArgs,

    /// Spans of all `#[macro_use]` arguments with arguments, used for linting
    uses_attr_spans: ThinVec<Span>,
    /// If `state` is `UseSpecific`, stores the span of the first `#[macro_use]` argument, used as the span for this attribute
    /// If `state` is `UseAll`, stores the span of the first `#[macro_use]` arguments without arguments
    first_span: Option<Span>,
}

const MACRO_USE_TEMPLATE: AttributeTemplate = template!(
    Word, List: &["name1, name2, ..."],
    "https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute"
);
const MACRO_USE_ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
    Allow(Target::Mod),
    Allow(Target::ExternCrate),
    Allow(Target::Crate),
    Error(Target::WherePredicate),
]);

impl<S: Stage> AttributeParser<S> for MacroUseParser {
    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
        &[sym::macro_use],
        MACRO_USE_TEMPLATE,
        |group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| {
            let span = cx.attr_span;
            group.first_span.get_or_insert(span);
            match args {
                ArgParser::NoArgs => {
                    match group.state {
                        MacroUseArgs::UseAll => {
                            let first_span = group.first_span.expect(
                                "State is UseAll is some so this is not the first attribute",
                            );
                            // Since there is a `#[macro_use]` import already, give a warning
                            cx.warn_unused_duplicate(first_span, span);
                        }
                        MacroUseArgs::UseSpecific(_) => {
                            group.state = MacroUseArgs::UseAll;
                            group.first_span = Some(span);
                            // If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
                            for specific_use in group.uses_attr_spans.drain(..) {
                                cx.warn_unused_duplicate(span, specific_use);
                            }
                        }
                    }
                }
                ArgParser::List(list) => {
                    if list.is_empty() {
                        cx.warn_empty_attribute(list.span);
                        return;
                    }

                    match &mut group.state {
                        MacroUseArgs::UseAll => {
                            let first_span = group.first_span.expect(
                                "State is UseAll is some so this is not the first attribute",
                            );
                            cx.warn_unused_duplicate(first_span, span);
                        }
                        MacroUseArgs::UseSpecific(arguments) => {
                            // Store here so if we encounter a `UseAll` later we can still lint this attribute
                            group.uses_attr_spans.push(cx.attr_span);

                            for item in list.mixed() {
                                let Some(item) = item.meta_item() else {
                                    cx.expected_identifier(item.span());
                                    continue;
                                };
                                if let Err(err_span) = item.args().no_args() {
                                    cx.expected_no_args(err_span);
                                    continue;
                                }
                                let Some(item) = item.path().word() else {
                                    cx.expected_identifier(item.span());
                                    continue;
                                };
                                arguments.push(item);
                            }
                        }
                    }
                }
                ArgParser::NameValue(_) => {
                    let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
                    cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
                        num_suggestions: suggestions.len(),
                        suggestions: DiagArgValue::StrListSepByAnd(
                            suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
                        ),
                        span,
                    });
                }
            }
        },
    )];
    const ALLOWED_TARGETS: AllowedTargets = MACRO_USE_ALLOWED_TARGETS;

    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
        Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
    }
}

pub(crate) struct AllowInternalUnsafeParser;

impl<S: Stage> NoArgsAttributeParser<S> for AllowInternalUnsafeParser {
    const PATH: &[Symbol] = &[sym::allow_internal_unsafe];
    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
        Allow(Target::Fn),
        Allow(Target::MacroDef),
        Warn(Target::Field),
        Warn(Target::Arm),
    ]);
    const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span);
}