rustc_attr_parsing/attributes/
codegen_attrs.rs

1use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy};
2use rustc_session::parse::feature_err;
3
4use super::prelude::*;
5use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
6
7pub(crate) struct OptimizeParser;
8
9impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
10    const PATH: &[Symbol] = &[sym::optimize];
11    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
12    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
13    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
14        Allow(Target::Fn),
15        Allow(Target::Closure),
16        Allow(Target::Method(MethodKind::Trait { body: true })),
17        Allow(Target::Method(MethodKind::TraitImpl)),
18        Allow(Target::Method(MethodKind::Inherent)),
19    ]);
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(), &[sym::size, sym::speed, sym::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 ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
53        Allow(Target::Fn),
54        Allow(Target::Method(MethodKind::Trait { body: true })),
55        Allow(Target::Method(MethodKind::TraitImpl)),
56        Allow(Target::Method(MethodKind::Trait { body: false })),
57        Allow(Target::Method(MethodKind::Inherent)),
58        Allow(Target::ForeignFn),
59        Allow(Target::Closure),
60    ]);
61    const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
62}
63
64pub(crate) struct CoverageParser;
65
66impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
67    const PATH: &[Symbol] = &[sym::coverage];
68    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
69    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
70    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
71        Allow(Target::Fn),
72        Allow(Target::Closure),
73        Allow(Target::Method(MethodKind::Trait { body: true })),
74        Allow(Target::Method(MethodKind::TraitImpl)),
75        Allow(Target::Method(MethodKind::Inherent)),
76        Allow(Target::Impl { of_trait: true }),
77        Allow(Target::Impl { of_trait: false }),
78        Allow(Target::Mod),
79        Allow(Target::Crate),
80    ]);
81    const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
82
83    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
84        let Some(args) = args.list() else {
85            cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]);
86            return None;
87        };
88
89        let Some(arg) = args.single() else {
90            cx.expected_single_argument(args.span);
91            return None;
92        };
93
94        let fail_incorrect_argument =
95            |span| cx.expected_specific_argument(span, &[sym::on, sym::off]);
96
97        let Some(arg) = arg.meta_item() else {
98            fail_incorrect_argument(args.span);
99            return None;
100        };
101
102        let kind = match arg.path().word_sym() {
103            Some(sym::off) => CoverageAttrKind::Off,
104            Some(sym::on) => CoverageAttrKind::On,
105            None | Some(_) => {
106                fail_incorrect_argument(arg.span());
107                return None;
108            }
109        };
110
111        Some(AttributeKind::Coverage(cx.attr_span, kind))
112    }
113}
114
115pub(crate) struct ExportNameParser;
116
117impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
118    const PATH: &[rustc_span::Symbol] = &[sym::export_name];
119    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
120    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
121    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
122        Allow(Target::Static),
123        Allow(Target::Fn),
124        Allow(Target::Method(MethodKind::Inherent)),
125        Allow(Target::Method(MethodKind::Trait { body: true })),
126        Allow(Target::Method(MethodKind::TraitImpl)),
127        Warn(Target::Field),
128        Warn(Target::Arm),
129        Warn(Target::MacroDef),
130        Warn(Target::MacroCall),
131    ]);
132    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
133
134    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
135        let Some(nv) = args.name_value() else {
136            cx.expected_name_value(cx.attr_span, None);
137            return None;
138        };
139        let Some(name) = nv.value_as_str() else {
140            cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
141            return None;
142        };
143        if name.as_str().contains('\0') {
144            // `#[export_name = ...]` will be converted to a null-terminated string,
145            // so it may not contain any null characters.
146            cx.emit_err(NullOnExport { span: cx.attr_span });
147            return None;
148        }
149        Some(AttributeKind::ExportName { name, span: cx.attr_span })
150    }
151}
152
153#[derive(Default)]
154pub(crate) struct NakedParser {
155    span: Option<Span>,
156}
157
158impl<S: Stage> AttributeParser<S> for NakedParser {
159    const ATTRIBUTES: AcceptMapping<Self, S> =
160        &[(&[sym::naked], template!(Word), |this, cx, args| {
161            if let Err(span) = args.no_args() {
162                cx.expected_no_args(span);
163                return;
164            }
165
166            if let Some(earlier) = this.span {
167                let span = cx.attr_span;
168                cx.warn_unused_duplicate(earlier, span);
169            } else {
170                this.span = Some(cx.attr_span);
171            }
172        })];
173    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
174        Allow(Target::Fn),
175        Allow(Target::Method(MethodKind::Inherent)),
176        Allow(Target::Method(MethodKind::Trait { body: true })),
177        Allow(Target::Method(MethodKind::TraitImpl)),
178        Warn(Target::MacroCall),
179    ]);
180
181    fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
182        // FIXME(jdonszelmann): upgrade this list to *parsed* attributes
183        // once all of these have parsed forms. That'd make the check much nicer...
184        //
185        // many attributes don't make sense in combination with #[naked].
186        // Notable attributes that are incompatible with `#[naked]` are:
187        //
188        // * `#[inline]`
189        // * `#[track_caller]`
190        // * `#[test]`, `#[ignore]`, `#[should_panic]`
191        //
192        // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
193        // accurate.
194        const ALLOW_LIST: &[rustc_span::Symbol] = &[
195            // conditional compilation
196            sym::cfg_trace,
197            sym::cfg_attr_trace,
198            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
199            sym::test,
200            sym::ignore,
201            sym::should_panic,
202            sym::bench,
203            // diagnostics
204            sym::allow,
205            sym::warn,
206            sym::deny,
207            sym::forbid,
208            sym::deprecated,
209            sym::must_use,
210            // abi, linking and FFI
211            sym::cold,
212            sym::export_name,
213            sym::link_section,
214            sym::linkage,
215            sym::no_mangle,
216            sym::instruction_set,
217            sym::repr,
218            sym::rustc_std_internal_symbol,
219            // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
220            sym::rustc_align,
221            sym::rustc_align_static,
222            // obviously compatible with self
223            sym::naked,
224            // documentation
225            sym::doc,
226        ];
227
228        let span = self.span?;
229
230        // only if we found a naked attribute do we do the somewhat expensive check
231        'outer: for other_attr in cx.all_attrs {
232            for allowed_attr in ALLOW_LIST {
233                if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
234                    // effectively skips the error message  being emitted below
235                    // if it's a tool attribute
236                    continue 'outer;
237                }
238                if other_attr.word_is(*allowed_attr) {
239                    // effectively skips the error message  being emitted below
240                    // if its an allowed attribute
241                    continue 'outer;
242                }
243
244                if other_attr.word_is(sym::target_feature) {
245                    if !cx.features().naked_functions_target_feature() {
246                        feature_err(
247                            &cx.sess(),
248                            sym::naked_functions_target_feature,
249                            other_attr.span(),
250                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
251                        ).emit();
252                    }
253
254                    continue 'outer;
255                }
256            }
257
258            cx.emit_err(NakedFunctionIncompatibleAttribute {
259                span: other_attr.span(),
260                naked_span: span,
261                attr: other_attr.get_attribute_path().to_string(),
262            });
263        }
264
265        Some(AttributeKind::Naked(span))
266    }
267}
268
269pub(crate) struct TrackCallerParser;
270impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
271    const PATH: &[Symbol] = &[sym::track_caller];
272    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
273    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
274        Allow(Target::Fn),
275        Allow(Target::Method(MethodKind::Inherent)),
276        Allow(Target::Method(MethodKind::Trait { body: true })),
277        Allow(Target::Method(MethodKind::TraitImpl)),
278        Allow(Target::Method(MethodKind::Trait { body: false })),
279        Allow(Target::ForeignFn),
280        Allow(Target::Closure),
281        Warn(Target::MacroDef),
282        Warn(Target::Arm),
283        Warn(Target::Field),
284        Warn(Target::MacroCall),
285    ]);
286    const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
287}
288
289pub(crate) struct NoMangleParser;
290impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
291    const PATH: &[Symbol] = &[sym::no_mangle];
292    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
293    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
294        Allow(Target::Fn),
295        Allow(Target::Static),
296        Allow(Target::Method(MethodKind::Inherent)),
297        Allow(Target::Method(MethodKind::TraitImpl)),
298    ]);
299    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
300}
301
302#[derive(Default)]
303pub(crate) struct UsedParser {
304    first_compiler: Option<Span>,
305    first_linker: Option<Span>,
306}
307
308// A custom `AttributeParser` is used rather than a Simple attribute parser because
309// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
310// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
311// We can change this to a Simple parser once the warning becomes an error
312impl<S: Stage> AttributeParser<S> for UsedParser {
313    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
314        &[sym::used],
315        template!(Word, List: &["compiler", "linker"]),
316        |group: &mut Self, cx, args| {
317            let used_by = match args {
318                ArgParser::NoArgs => UsedBy::Linker,
319                ArgParser::List(list) => {
320                    let Some(l) = list.single() else {
321                        cx.expected_single_argument(list.span);
322                        return;
323                    };
324
325                    match l.meta_item().and_then(|i| i.path().word_sym()) {
326                        Some(sym::compiler) => {
327                            if !cx.features().used_with_arg() {
328                                feature_err(
329                                    &cx.sess(),
330                                    sym::used_with_arg,
331                                    cx.attr_span,
332                                    "`#[used(compiler)]` is currently unstable",
333                                )
334                                .emit();
335                            }
336                            UsedBy::Compiler
337                        }
338                        Some(sym::linker) => {
339                            if !cx.features().used_with_arg() {
340                                feature_err(
341                                    &cx.sess(),
342                                    sym::used_with_arg,
343                                    cx.attr_span,
344                                    "`#[used(linker)]` is currently unstable",
345                                )
346                                .emit();
347                            }
348                            UsedBy::Linker
349                        }
350                        _ => {
351                            cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
352                            return;
353                        }
354                    }
355                }
356                ArgParser::NameValue(_) => return,
357            };
358
359            let target = match used_by {
360                UsedBy::Compiler => &mut group.first_compiler,
361                UsedBy::Linker => &mut group.first_linker,
362            };
363
364            let attr_span = cx.attr_span;
365            if let Some(prev) = *target {
366                cx.warn_unused_duplicate(prev, attr_span);
367            } else {
368                *target = Some(attr_span);
369            }
370        },
371    )];
372    const ALLOWED_TARGETS: AllowedTargets =
373        AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
374
375    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
376        // Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
377        Some(match (self.first_compiler, self.first_linker) {
378            (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
379            (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
380            (None, None) => return None,
381        })
382    }
383}
384
385fn parse_tf_attribute<'c, S: Stage>(
386    cx: &'c mut AcceptContext<'_, '_, S>,
387    args: &'c ArgParser<'_>,
388) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
389    let mut features = Vec::new();
390    let ArgParser::List(list) = args else {
391        cx.expected_list(cx.attr_span);
392        return features;
393    };
394    if list.is_empty() {
395        cx.warn_empty_attribute(cx.attr_span);
396        return features;
397    }
398    for item in list.mixed() {
399        let Some(name_value) = item.meta_item() else {
400            cx.expected_name_value(item.span(), Some(sym::enable));
401            return features;
402        };
403
404        // Validate name
405        let Some(name) = name_value.path().word_sym() else {
406            cx.expected_name_value(name_value.path().span(), Some(sym::enable));
407            return features;
408        };
409        if name != sym::enable {
410            cx.expected_name_value(name_value.path().span(), Some(sym::enable));
411            return features;
412        }
413
414        // Use value
415        let Some(name_value) = name_value.args().name_value() else {
416            cx.expected_name_value(item.span(), Some(sym::enable));
417            return features;
418        };
419        let Some(value_str) = name_value.value_as_str() else {
420            cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
421            return features;
422        };
423        for feature in value_str.as_str().split(",") {
424            features.push((Symbol::intern(feature), item.span()));
425        }
426    }
427    features
428}
429
430pub(crate) struct TargetFeatureParser;
431
432impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
433    type Item = (Symbol, Span);
434    const PATH: &[Symbol] = &[sym::target_feature];
435    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
436        features: items,
437        attr_span: span,
438        was_forced: false,
439    };
440    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
441
442    fn extend<'c>(
443        cx: &'c mut AcceptContext<'_, '_, S>,
444        args: &'c ArgParser<'_>,
445    ) -> impl IntoIterator<Item = Self::Item> + 'c {
446        parse_tf_attribute(cx, args)
447    }
448
449    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
450        Allow(Target::Fn),
451        Allow(Target::Method(MethodKind::Inherent)),
452        Allow(Target::Method(MethodKind::Trait { body: true })),
453        Allow(Target::Method(MethodKind::TraitImpl)),
454        Warn(Target::Statement),
455        Warn(Target::Field),
456        Warn(Target::Arm),
457        Warn(Target::MacroDef),
458        Warn(Target::MacroCall),
459    ]);
460}
461
462pub(crate) struct ForceTargetFeatureParser;
463
464impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
465    type Item = (Symbol, Span);
466    const PATH: &[Symbol] = &[sym::force_target_feature];
467    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
468        features: items,
469        attr_span: span,
470        was_forced: true,
471    };
472    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
473    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
474        Allow(Target::Fn),
475        Allow(Target::Method(MethodKind::Inherent)),
476        Allow(Target::Method(MethodKind::Trait { body: true })),
477        Allow(Target::Method(MethodKind::TraitImpl)),
478    ]);
479
480    fn extend<'c>(
481        cx: &'c mut AcceptContext<'_, '_, S>,
482        args: &'c ArgParser<'_>,
483    ) -> impl IntoIterator<Item = Self::Item> + 'c {
484        parse_tf_attribute(cx, args)
485    }
486}
487
488pub(crate) struct SanitizeParser;
489
490impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
491    const PATH: &[Symbol] = &[sym::sanitize];
492
493    // FIXME: still checked in check_attrs.rs
494    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
495
496    const TEMPLATE: AttributeTemplate = template!(List: &[
497        r#"address = "on|off""#,
498        r#"kernel_address = "on|off""#,
499        r#"cfi = "on|off""#,
500        r#"hwaddress = "on|off""#,
501        r#"kcfi = "on|off""#,
502        r#"memory = "on|off""#,
503        r#"memtag = "on|off""#,
504        r#"shadow_call_stack = "on|off""#,
505        r#"thread = "on|off""#
506    ]);
507
508    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
509    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
510
511    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
512        let Some(list) = args.list() else {
513            cx.expected_list(cx.attr_span);
514            return None;
515        };
516
517        let mut on_set = SanitizerSet::empty();
518        let mut off_set = SanitizerSet::empty();
519
520        for item in list.mixed() {
521            let Some(item) = item.meta_item() else {
522                cx.expected_name_value(item.span(), None);
523                continue;
524            };
525
526            let path = item.path().word_sym();
527            let Some(value) = item.args().name_value() else {
528                cx.expected_name_value(item.span(), path);
529                continue;
530            };
531
532            let mut apply = |s: SanitizerSet| {
533                let is_on = match value.value_as_str() {
534                    Some(sym::on) => true,
535                    Some(sym::off) => false,
536                    Some(_) => {
537                        cx.expected_specific_argument_strings(
538                            value.value_span,
539                            &[sym::on, sym::off],
540                        );
541                        return;
542                    }
543                    None => {
544                        cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
545                        return;
546                    }
547                };
548
549                if is_on {
550                    on_set |= s;
551                } else {
552                    off_set |= s;
553                }
554            };
555
556            match path {
557                Some(sym::address) | Some(sym::kernel_address) => {
558                    apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
559                }
560                Some(sym::cfi) => apply(SanitizerSet::CFI),
561                Some(sym::kcfi) => apply(SanitizerSet::KCFI),
562                Some(sym::memory) => apply(SanitizerSet::MEMORY),
563                Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
564                Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
565                Some(sym::thread) => apply(SanitizerSet::THREAD),
566                Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
567                _ => {
568                    cx.expected_specific_argument_strings(
569                        item.path().span(),
570                        &[
571                            sym::address,
572                            sym::cfi,
573                            sym::kcfi,
574                            sym::memory,
575                            sym::memtag,
576                            sym::shadow_call_stack,
577                            sym::thread,
578                            sym::hwaddress,
579                        ],
580                    );
581                    continue;
582                }
583            }
584        }
585
586        Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
587    }
588}