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            // obviously compatible with self
222            sym::naked,
223            // documentation
224            sym::doc,
225        ];
226
227        let span = self.span?;
228
229        // only if we found a naked attribute do we do the somewhat expensive check
230        'outer: for other_attr in cx.all_attrs {
231            for allowed_attr in ALLOW_LIST {
232                if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
233                    // effectively skips the error message  being emitted below
234                    // if it's a tool attribute
235                    continue 'outer;
236                }
237                if other_attr.word_is(*allowed_attr) {
238                    // effectively skips the error message  being emitted below
239                    // if its an allowed attribute
240                    continue 'outer;
241                }
242
243                if other_attr.word_is(sym::target_feature) {
244                    if !cx.features().naked_functions_target_feature() {
245                        feature_err(
246                            &cx.sess(),
247                            sym::naked_functions_target_feature,
248                            other_attr.span(),
249                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
250                        ).emit();
251                    }
252
253                    continue 'outer;
254                }
255            }
256
257            cx.emit_err(NakedFunctionIncompatibleAttribute {
258                span: other_attr.span(),
259                naked_span: span,
260                attr: other_attr.get_attribute_path().to_string(),
261            });
262        }
263
264        Some(AttributeKind::Naked(span))
265    }
266}
267
268pub(crate) struct TrackCallerParser;
269impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
270    const PATH: &[Symbol] = &[sym::track_caller];
271    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
272    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
273        Allow(Target::Fn),
274        Allow(Target::Method(MethodKind::Inherent)),
275        Allow(Target::Method(MethodKind::Trait { body: true })),
276        Allow(Target::Method(MethodKind::TraitImpl)),
277        Allow(Target::Method(MethodKind::Trait { body: false })),
278        Allow(Target::ForeignFn),
279        Allow(Target::Closure),
280        Warn(Target::MacroDef),
281        Warn(Target::Arm),
282        Warn(Target::Field),
283        Warn(Target::MacroCall),
284    ]);
285    const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
286}
287
288pub(crate) struct NoMangleParser;
289impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
290    const PATH: &[Symbol] = &[sym::no_mangle];
291    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
292    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
293        Allow(Target::Fn),
294        Allow(Target::Static),
295        Allow(Target::Method(MethodKind::Inherent)),
296        Allow(Target::Method(MethodKind::TraitImpl)),
297    ]);
298    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
299}
300
301#[derive(Default)]
302pub(crate) struct UsedParser {
303    first_compiler: Option<Span>,
304    first_linker: Option<Span>,
305}
306
307// A custom `AttributeParser` is used rather than a Simple attribute parser because
308// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
309// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
310// We can change this to a Simple parser once the warning becomes an error
311impl<S: Stage> AttributeParser<S> for UsedParser {
312    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
313        &[sym::used],
314        template!(Word, List: &["compiler", "linker"]),
315        |group: &mut Self, cx, args| {
316            let used_by = match args {
317                ArgParser::NoArgs => UsedBy::Linker,
318                ArgParser::List(list) => {
319                    let Some(l) = list.single() else {
320                        cx.expected_single_argument(list.span);
321                        return;
322                    };
323
324                    match l.meta_item().and_then(|i| i.path().word_sym()) {
325                        Some(sym::compiler) => {
326                            if !cx.features().used_with_arg() {
327                                feature_err(
328                                    &cx.sess(),
329                                    sym::used_with_arg,
330                                    cx.attr_span,
331                                    "`#[used(compiler)]` is currently unstable",
332                                )
333                                .emit();
334                            }
335                            UsedBy::Compiler
336                        }
337                        Some(sym::linker) => {
338                            if !cx.features().used_with_arg() {
339                                feature_err(
340                                    &cx.sess(),
341                                    sym::used_with_arg,
342                                    cx.attr_span,
343                                    "`#[used(linker)]` is currently unstable",
344                                )
345                                .emit();
346                            }
347                            UsedBy::Linker
348                        }
349                        _ => {
350                            cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
351                            return;
352                        }
353                    }
354                }
355                ArgParser::NameValue(_) => return,
356            };
357
358            let target = match used_by {
359                UsedBy::Compiler => &mut group.first_compiler,
360                UsedBy::Linker => &mut group.first_linker,
361            };
362
363            let attr_span = cx.attr_span;
364            if let Some(prev) = *target {
365                cx.warn_unused_duplicate(prev, attr_span);
366            } else {
367                *target = Some(attr_span);
368            }
369        },
370    )];
371    const ALLOWED_TARGETS: AllowedTargets =
372        AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
373
374    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
375        // Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
376        Some(match (self.first_compiler, self.first_linker) {
377            (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
378            (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
379            (None, None) => return None,
380        })
381    }
382}
383
384fn parse_tf_attribute<'c, S: Stage>(
385    cx: &'c mut AcceptContext<'_, '_, S>,
386    args: &'c ArgParser<'_>,
387) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
388    let mut features = Vec::new();
389    let ArgParser::List(list) = args else {
390        cx.expected_list(cx.attr_span);
391        return features;
392    };
393    if list.is_empty() {
394        cx.warn_empty_attribute(cx.attr_span);
395        return features;
396    }
397    for item in list.mixed() {
398        let Some(name_value) = item.meta_item() else {
399            cx.expected_name_value(item.span(), Some(sym::enable));
400            return features;
401        };
402
403        // Validate name
404        let Some(name) = name_value.path().word_sym() else {
405            cx.expected_name_value(name_value.path().span(), Some(sym::enable));
406            return features;
407        };
408        if name != sym::enable {
409            cx.expected_name_value(name_value.path().span(), Some(sym::enable));
410            return features;
411        }
412
413        // Use value
414        let Some(name_value) = name_value.args().name_value() else {
415            cx.expected_name_value(item.span(), Some(sym::enable));
416            return features;
417        };
418        let Some(value_str) = name_value.value_as_str() else {
419            cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
420            return features;
421        };
422        for feature in value_str.as_str().split(",") {
423            features.push((Symbol::intern(feature), item.span()));
424        }
425    }
426    features
427}
428
429pub(crate) struct TargetFeatureParser;
430
431impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
432    type Item = (Symbol, Span);
433    const PATH: &[Symbol] = &[sym::target_feature];
434    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
435        features: items,
436        attr_span: span,
437        was_forced: false,
438    };
439    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
440
441    fn extend<'c>(
442        cx: &'c mut AcceptContext<'_, '_, S>,
443        args: &'c ArgParser<'_>,
444    ) -> impl IntoIterator<Item = Self::Item> + 'c {
445        parse_tf_attribute(cx, args)
446    }
447
448    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
449        Allow(Target::Fn),
450        Allow(Target::Method(MethodKind::Inherent)),
451        Allow(Target::Method(MethodKind::Trait { body: true })),
452        Allow(Target::Method(MethodKind::TraitImpl)),
453        Warn(Target::Statement),
454        Warn(Target::Field),
455        Warn(Target::Arm),
456        Warn(Target::MacroDef),
457        Warn(Target::MacroCall),
458    ]);
459}
460
461pub(crate) struct ForceTargetFeatureParser;
462
463impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
464    type Item = (Symbol, Span);
465    const PATH: &[Symbol] = &[sym::force_target_feature];
466    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
467        features: items,
468        attr_span: span,
469        was_forced: true,
470    };
471    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
472    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
473        Allow(Target::Fn),
474        Allow(Target::Method(MethodKind::Inherent)),
475        Allow(Target::Method(MethodKind::Trait { body: true })),
476        Allow(Target::Method(MethodKind::TraitImpl)),
477    ]);
478
479    fn extend<'c>(
480        cx: &'c mut AcceptContext<'_, '_, S>,
481        args: &'c ArgParser<'_>,
482    ) -> impl IntoIterator<Item = Self::Item> + 'c {
483        parse_tf_attribute(cx, args)
484    }
485}
486
487pub(crate) struct SanitizeParser;
488
489impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
490    const PATH: &[Symbol] = &[sym::sanitize];
491
492    // FIXME: still checked in check_attrs.rs
493    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
494
495    const TEMPLATE: AttributeTemplate = template!(List: &[
496        r#"address = "on|off""#,
497        r#"kernel_address = "on|off""#,
498        r#"cfi = "on|off""#,
499        r#"hwaddress = "on|off""#,
500        r#"kcfi = "on|off""#,
501        r#"memory = "on|off""#,
502        r#"memtag = "on|off""#,
503        r#"shadow_call_stack = "on|off""#,
504        r#"thread = "on|off""#
505    ]);
506
507    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
508    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
509
510    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
511        let Some(list) = args.list() else {
512            cx.expected_list(cx.attr_span);
513            return None;
514        };
515
516        let mut on_set = SanitizerSet::empty();
517        let mut off_set = SanitizerSet::empty();
518
519        for item in list.mixed() {
520            let Some(item) = item.meta_item() else {
521                cx.expected_name_value(item.span(), None);
522                continue;
523            };
524
525            let path = item.path().word_sym();
526            let Some(value) = item.args().name_value() else {
527                cx.expected_name_value(item.span(), path);
528                continue;
529            };
530
531            let mut apply = |s: SanitizerSet| {
532                let is_on = match value.value_as_str() {
533                    Some(sym::on) => true,
534                    Some(sym::off) => false,
535                    Some(_) => {
536                        cx.expected_specific_argument_strings(
537                            value.value_span,
538                            &[sym::on, sym::off],
539                        );
540                        return;
541                    }
542                    None => {
543                        cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
544                        return;
545                    }
546                };
547
548                if is_on {
549                    on_set |= s;
550                } else {
551                    off_set |= s;
552                }
553            };
554
555            match path {
556                Some(sym::address) | Some(sym::kernel_address) => {
557                    apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
558                }
559                Some(sym::cfi) => apply(SanitizerSet::CFI),
560                Some(sym::kcfi) => apply(SanitizerSet::KCFI),
561                Some(sym::memory) => apply(SanitizerSet::MEMORY),
562                Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
563                Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
564                Some(sym::thread) => apply(SanitizerSet::THREAD),
565                Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
566                _ => {
567                    cx.expected_specific_argument_strings(
568                        item.path().span(),
569                        &[
570                            sym::address,
571                            sym::cfi,
572                            sym::kcfi,
573                            sym::memory,
574                            sym::memtag,
575                            sym::shadow_call_stack,
576                            sym::thread,
577                            sym::hwaddress,
578                        ],
579                    );
580                    continue;
581                }
582            }
583        }
584
585        Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
586    }
587}