about summary refs log tree commit diff
path: root/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
blob: 0779248e1a95e21ad6d31c6eb6fbf3a81021b24e (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
use rustc_errors::DiagArgValue;
use rustc_feature::{AttributeTemplate, template};
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::{AcceptContext, 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 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, ...");

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(false, 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,
                    });
                }
            }
        },
    )];

    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 CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span);
}