rustc_attr_parsing/attributes/
codegen_attrs.rs

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