about summary refs log tree commit diff
path: root/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
blob: 24c40c301fe08c0c870318b3c51ddd9b07000367 (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
use rustc_feature::{AttributeTemplate, template};
use rustc_session::parse::feature_err;
use rustc_span::{Span, sym};

use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, FinalizeContext, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics::NakedFunctionIncompatibleAttribute;

pub(crate) struct OptimizeParser;

impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
    const PATH: &[rustc_span::Symbol] = &[sym::optimize];
    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
    const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");

    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
        let Some(list) = args.list() else {
            cx.expected_list(cx.attr_span);
            return None;
        };

        let Some(single) = list.single() else {
            cx.expected_single_argument(list.span);
            return None;
        };

        let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
            Some(sym::size) => OptimizeAttr::Size,
            Some(sym::speed) => OptimizeAttr::Speed,
            Some(sym::none) => OptimizeAttr::DoNotOptimize,
            _ => {
                cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
                OptimizeAttr::Default
            }
        };

        Some(AttributeKind::Optimize(res, cx.attr_span))
    }
}

pub(crate) struct ColdParser;

impl<S: Stage> SingleAttributeParser<S> for ColdParser {
    const PATH: &[rustc_span::Symbol] = &[sym::cold];
    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
    const TEMPLATE: AttributeTemplate = template!(Word);

    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
        if !args.no_args() {
            cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
            return None;
        }

        Some(AttributeKind::Cold(cx.attr_span))
    }
}

#[derive(Default)]
pub(crate) struct NakedParser {
    span: Option<Span>,
}

impl<S: Stage> AttributeParser<S> for NakedParser {
    const ATTRIBUTES: AcceptMapping<Self, S> =
        &[(&[sym::naked], template!(Word), |this, cx, args| {
            if !args.no_args() {
                cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
                return;
            }

            if let Some(earlier) = this.span {
                let span = cx.attr_span;
                cx.warn_unused_duplicate(earlier, span);
            } else {
                this.span = Some(cx.attr_span);
            }
        })];

    fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
        // FIXME(jdonszelmann): upgrade this list to *parsed* attributes
        // once all of these have parsed forms. That'd make the check much nicer...
        //
        // many attributes don't make sense in combination with #[naked].
        // Notable attributes that are incompatible with `#[naked]` are:
        //
        // * `#[inline]`
        // * `#[track_caller]`
        // * `#[test]`, `#[ignore]`, `#[should_panic]`
        //
        // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
        // accurate.
        const ALLOW_LIST: &[rustc_span::Symbol] = &[
            // conditional compilation
            sym::cfg_trace,
            sym::cfg_attr_trace,
            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
            sym::test,
            sym::ignore,
            sym::should_panic,
            sym::bench,
            // diagnostics
            sym::allow,
            sym::warn,
            sym::deny,
            sym::forbid,
            sym::deprecated,
            sym::must_use,
            // abi, linking and FFI
            sym::cold,
            sym::export_name,
            sym::link_section,
            sym::linkage,
            sym::no_mangle,
            sym::instruction_set,
            sym::repr,
            sym::rustc_std_internal_symbol,
            sym::align,
            // obviously compatible with self
            sym::naked,
            // documentation
            sym::doc,
        ];

        let span = self.span?;

        // only if we found a naked attribute do we do the somewhat expensive check
        'outer: for other_attr in cx.all_attrs {
            for allowed_attr in ALLOW_LIST {
                if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
                    // effectively skips the error message  being emitted below
                    // if it's a tool attribute
                    continue 'outer;
                }
                if other_attr.word_is(*allowed_attr) {
                    // effectively skips the error message  being emitted below
                    // if its an allowed attribute
                    continue 'outer;
                }

                if other_attr.word_is(sym::target_feature) {
                    if !cx.features().naked_functions_target_feature() {
                        feature_err(
                            &cx.sess(),
                            sym::naked_functions_target_feature,
                            other_attr.span(),
                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
                        ).emit();
                    }

                    continue 'outer;
                }
            }

            cx.emit_err(NakedFunctionIncompatibleAttribute {
                span: other_attr.span(),
                naked_span: span,
                attr: other_attr.get_attribute_path().to_string(),
            });
        }

        Some(AttributeKind::Naked(span))
    }
}

pub(crate) struct NoMangleParser;

impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {
    const PATH: &[rustc_span::Symbol] = &[sym::no_mangle];
    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
    const TEMPLATE: AttributeTemplate = template!(Word);

    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
        if !args.no_args() {
            cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
            return None;
        };

        Some(AttributeKind::NoMangle(cx.attr_span))
    }
}