rustc_attr_parsing/attributes/
codegen_attrs.rs

1use rustc_attr_data_structures::{AttributeKind, OptimizeAttr, UsedBy};
2use rustc_feature::{AttributeTemplate, template};
3use rustc_session::parse::feature_err;
4use rustc_span::{Span, Symbol, sym};
5
6use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
7use crate::context::{AcceptContext, FinalizeContext, Stage};
8use crate::parser::ArgParser;
9use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
10
11pub(crate) struct OptimizeParser;
12
13impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
14    const PATH: &[Symbol] = &[sym::optimize];
15    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
16    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
17    const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
18
19    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
20        let Some(list) = args.list() else {
21            cx.expected_list(cx.attr_span);
22            return None;
23        };
24
25        let Some(single) = list.single() else {
26            cx.expected_single_argument(list.span);
27            return None;
28        };
29
30        let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
31            Some(sym::size) => OptimizeAttr::Size,
32            Some(sym::speed) => OptimizeAttr::Speed,
33            Some(sym::none) => OptimizeAttr::DoNotOptimize,
34            _ => {
35                cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
36                OptimizeAttr::Default
37            }
38        };
39
40        Some(AttributeKind::Optimize(res, cx.attr_span))
41    }
42}
43
44pub(crate) struct ColdParser;
45
46impl<S: Stage> SingleAttributeParser<S> for ColdParser {
47    const PATH: &[Symbol] = &[sym::cold];
48    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
49    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
50    const TEMPLATE: AttributeTemplate = template!(Word);
51
52    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
53        if let Err(span) = args.no_args() {
54            cx.expected_no_args(span);
55            return None;
56        }
57
58        Some(AttributeKind::Cold(cx.attr_span))
59    }
60}
61
62pub(crate) struct ExportNameParser;
63
64impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
65    const PATH: &[rustc_span::Symbol] = &[sym::export_name];
66    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
67    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
68    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
69
70    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
71        let Some(nv) = args.name_value() else {
72            cx.expected_name_value(cx.attr_span, None);
73            return None;
74        };
75        let Some(name) = nv.value_as_str() else {
76            cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
77            return None;
78        };
79        if name.as_str().contains('\0') {
80            // `#[export_name = ...]` will be converted to a null-terminated string,
81            // so it may not contain any null characters.
82            cx.emit_err(NullOnExport { span: cx.attr_span });
83            return None;
84        }
85        Some(AttributeKind::ExportName { name, span: cx.attr_span })
86    }
87}
88
89#[derive(Default)]
90pub(crate) struct NakedParser {
91    span: Option<Span>,
92}
93
94impl<S: Stage> AttributeParser<S> for NakedParser {
95    const ATTRIBUTES: AcceptMapping<Self, S> =
96        &[(&[sym::naked], template!(Word), |this, cx, args| {
97            if let Err(span) = args.no_args() {
98                cx.expected_no_args(span);
99                return;
100            }
101
102            if let Some(earlier) = this.span {
103                let span = cx.attr_span;
104                cx.warn_unused_duplicate(earlier, span);
105            } else {
106                this.span = Some(cx.attr_span);
107            }
108        })];
109
110    fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
111        // FIXME(jdonszelmann): upgrade this list to *parsed* attributes
112        // once all of these have parsed forms. That'd make the check much nicer...
113        //
114        // many attributes don't make sense in combination with #[naked].
115        // Notable attributes that are incompatible with `#[naked]` are:
116        //
117        // * `#[inline]`
118        // * `#[track_caller]`
119        // * `#[test]`, `#[ignore]`, `#[should_panic]`
120        //
121        // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
122        // accurate.
123        const ALLOW_LIST: &[rustc_span::Symbol] = &[
124            // conditional compilation
125            sym::cfg_trace,
126            sym::cfg_attr_trace,
127            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
128            sym::test,
129            sym::ignore,
130            sym::should_panic,
131            sym::bench,
132            // diagnostics
133            sym::allow,
134            sym::warn,
135            sym::deny,
136            sym::forbid,
137            sym::deprecated,
138            sym::must_use,
139            // abi, linking and FFI
140            sym::cold,
141            sym::export_name,
142            sym::link_section,
143            sym::linkage,
144            sym::no_mangle,
145            sym::instruction_set,
146            sym::repr,
147            sym::rustc_std_internal_symbol,
148            sym::align,
149            // obviously compatible with self
150            sym::naked,
151            // documentation
152            sym::doc,
153        ];
154
155        let span = self.span?;
156
157        // only if we found a naked attribute do we do the somewhat expensive check
158        'outer: for other_attr in cx.all_attrs {
159            for allowed_attr in ALLOW_LIST {
160                if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
161                    // effectively skips the error message  being emitted below
162                    // if it's a tool attribute
163                    continue 'outer;
164                }
165                if other_attr.word_is(*allowed_attr) {
166                    // effectively skips the error message  being emitted below
167                    // if its an allowed attribute
168                    continue 'outer;
169                }
170
171                if other_attr.word_is(sym::target_feature) {
172                    if !cx.features().naked_functions_target_feature() {
173                        feature_err(
174                            &cx.sess(),
175                            sym::naked_functions_target_feature,
176                            other_attr.span(),
177                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
178                        ).emit();
179                    }
180
181                    continue 'outer;
182                }
183            }
184
185            cx.emit_err(NakedFunctionIncompatibleAttribute {
186                span: other_attr.span(),
187                naked_span: span,
188                attr: other_attr.get_attribute_path().to_string(),
189            });
190        }
191
192        Some(AttributeKind::Naked(span))
193    }
194}
195
196pub(crate) struct TrackCallerParser;
197
198impl<S: Stage> SingleAttributeParser<S> for TrackCallerParser {
199    const PATH: &[Symbol] = &[sym::track_caller];
200    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
201    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
202    const TEMPLATE: AttributeTemplate = template!(Word);
203
204    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
205        if let Err(span) = args.no_args() {
206            cx.expected_no_args(span);
207            return None;
208        }
209
210        Some(AttributeKind::TrackCaller(cx.attr_span))
211    }
212}
213
214pub(crate) struct NoMangleParser;
215
216impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {
217    const PATH: &[rustc_span::Symbol] = &[sym::no_mangle];
218    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
219    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
220    const TEMPLATE: AttributeTemplate = template!(Word);
221
222    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
223        if let Err(span) = args.no_args() {
224            cx.expected_no_args(span);
225            return None;
226        }
227
228        Some(AttributeKind::NoMangle(cx.attr_span))
229    }
230}
231
232#[derive(Default)]
233pub(crate) struct UsedParser {
234    first_compiler: Option<Span>,
235    first_linker: Option<Span>,
236}
237
238// A custom `AttributeParser` is used rather than a Simple attribute parser because
239// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
240// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
241// We can change this to a Simple parser once the warning becomes an error
242impl<S: Stage> AttributeParser<S> for UsedParser {
243    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
244        &[sym::used],
245        template!(Word, List: "compiler|linker"),
246        |group: &mut Self, cx, args| {
247            let used_by = match args {
248                ArgParser::NoArgs => UsedBy::Linker,
249                ArgParser::List(list) => {
250                    let Some(l) = list.single() else {
251                        cx.expected_single_argument(list.span);
252                        return;
253                    };
254
255                    match l.meta_item().and_then(|i| i.path().word_sym()) {
256                        Some(sym::compiler) => {
257                            if !cx.features().used_with_arg() {
258                                feature_err(
259                                    &cx.sess(),
260                                    sym::used_with_arg,
261                                    cx.attr_span,
262                                    "`#[used(compiler)]` is currently unstable",
263                                )
264                                .emit();
265                            }
266                            UsedBy::Compiler
267                        }
268                        Some(sym::linker) => {
269                            if !cx.features().used_with_arg() {
270                                feature_err(
271                                    &cx.sess(),
272                                    sym::used_with_arg,
273                                    cx.attr_span,
274                                    "`#[used(linker)]` is currently unstable",
275                                )
276                                .emit();
277                            }
278                            UsedBy::Linker
279                        }
280                        _ => {
281                            cx.expected_specific_argument(l.span(), vec!["compiler", "linker"]);
282                            return;
283                        }
284                    }
285                }
286                ArgParser::NameValue(_) => return,
287            };
288
289            let target = match used_by {
290                UsedBy::Compiler => &mut group.first_compiler,
291                UsedBy::Linker => &mut group.first_linker,
292            };
293
294            let attr_span = cx.attr_span;
295            if let Some(prev) = *target {
296                cx.warn_unused_duplicate(prev, attr_span);
297            } else {
298                *target = Some(attr_span);
299            }
300        },
301    )];
302
303    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
304        // Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
305        Some(match (self.first_compiler, self.first_linker) {
306            (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
307            (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
308            (None, None) => return None,
309        })
310    }
311}