rustc_attr_parsing/attributes/
codegen_attrs.rs

1use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, 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    ]);
131    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
132
133    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
134        let Some(nv) = args.name_value() else {
135            cx.expected_name_value(cx.attr_span, None);
136            return None;
137        };
138        let Some(name) = nv.value_as_str() else {
139            cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
140            return None;
141        };
142        if name.as_str().contains('\0') {
143            // `#[export_name = ...]` will be converted to a null-terminated string,
144            // so it may not contain any null characters.
145            cx.emit_err(NullOnExport { span: cx.attr_span });
146            return None;
147        }
148        Some(AttributeKind::ExportName { name, span: cx.attr_span })
149    }
150}
151
152#[derive(Default)]
153pub(crate) struct NakedParser {
154    span: Option<Span>,
155}
156
157impl<S: Stage> AttributeParser<S> for NakedParser {
158    const ATTRIBUTES: AcceptMapping<Self, S> =
159        &[(&[sym::naked], template!(Word), |this, cx, args| {
160            if let Err(span) = args.no_args() {
161                cx.expected_no_args(span);
162                return;
163            }
164
165            if let Some(earlier) = this.span {
166                let span = cx.attr_span;
167                cx.warn_unused_duplicate(earlier, span);
168            } else {
169                this.span = Some(cx.attr_span);
170            }
171        })];
172    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
173        Allow(Target::Fn),
174        Allow(Target::Method(MethodKind::Inherent)),
175        Allow(Target::Method(MethodKind::Trait { body: true })),
176        Allow(Target::Method(MethodKind::TraitImpl)),
177    ]);
178
179    fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
180        // FIXME(jdonszelmann): upgrade this list to *parsed* attributes
181        // once all of these have parsed forms. That'd make the check much nicer...
182        //
183        // many attributes don't make sense in combination with #[naked].
184        // Notable attributes that are incompatible with `#[naked]` are:
185        //
186        // * `#[inline]`
187        // * `#[track_caller]`
188        // * `#[test]`, `#[ignore]`, `#[should_panic]`
189        //
190        // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
191        // accurate.
192        const ALLOW_LIST: &[rustc_span::Symbol] = &[
193            // conditional compilation
194            sym::cfg_trace,
195            sym::cfg_attr_trace,
196            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
197            sym::test,
198            sym::ignore,
199            sym::should_panic,
200            sym::bench,
201            // diagnostics
202            sym::allow,
203            sym::warn,
204            sym::deny,
205            sym::forbid,
206            sym::deprecated,
207            sym::must_use,
208            // abi, linking and FFI
209            sym::cold,
210            sym::export_name,
211            sym::link_section,
212            sym::linkage,
213            sym::no_mangle,
214            sym::instruction_set,
215            sym::repr,
216            sym::rustc_std_internal_symbol,
217            // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
218            sym::rustc_align,
219            // obviously compatible with self
220            sym::naked,
221            // documentation
222            sym::doc,
223        ];
224
225        let span = self.span?;
226
227        // only if we found a naked attribute do we do the somewhat expensive check
228        'outer: for other_attr in cx.all_attrs {
229            for allowed_attr in ALLOW_LIST {
230                if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
231                    // effectively skips the error message  being emitted below
232                    // if it's a tool attribute
233                    continue 'outer;
234                }
235                if other_attr.word_is(*allowed_attr) {
236                    // effectively skips the error message  being emitted below
237                    // if its an allowed attribute
238                    continue 'outer;
239                }
240
241                if other_attr.word_is(sym::target_feature) {
242                    if !cx.features().naked_functions_target_feature() {
243                        feature_err(
244                            &cx.sess(),
245                            sym::naked_functions_target_feature,
246                            other_attr.span(),
247                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
248                        ).emit();
249                    }
250
251                    continue 'outer;
252                }
253            }
254
255            cx.emit_err(NakedFunctionIncompatibleAttribute {
256                span: other_attr.span(),
257                naked_span: span,
258                attr: other_attr.get_attribute_path().to_string(),
259            });
260        }
261
262        Some(AttributeKind::Naked(span))
263    }
264}
265
266pub(crate) struct TrackCallerParser;
267impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
268    const PATH: &[Symbol] = &[sym::track_caller];
269    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
270    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
271        Allow(Target::Fn),
272        Allow(Target::Method(MethodKind::Inherent)),
273        Allow(Target::Method(MethodKind::Trait { body: true })),
274        Allow(Target::Method(MethodKind::TraitImpl)),
275        Allow(Target::Method(MethodKind::Trait { body: false })),
276        Allow(Target::ForeignFn),
277        Allow(Target::Closure),
278        Warn(Target::MacroDef),
279        Warn(Target::Arm),
280        Warn(Target::Field),
281    ]);
282    const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
283}
284
285pub(crate) struct NoMangleParser;
286impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
287    const PATH: &[Symbol] = &[sym::no_mangle];
288    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
289    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
290        Allow(Target::Fn),
291        Allow(Target::Static),
292        Allow(Target::Method(MethodKind::Inherent)),
293        Allow(Target::Method(MethodKind::TraitImpl)),
294    ]);
295    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
296}
297
298#[derive(Default)]
299pub(crate) struct UsedParser {
300    first_compiler: Option<Span>,
301    first_linker: Option<Span>,
302}
303
304// A custom `AttributeParser` is used rather than a Simple attribute parser because
305// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
306// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
307// We can change this to a Simple parser once the warning becomes an error
308impl<S: Stage> AttributeParser<S> for UsedParser {
309    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
310        &[sym::used],
311        template!(Word, List: &["compiler", "linker"]),
312        |group: &mut Self, cx, args| {
313            let used_by = match args {
314                ArgParser::NoArgs => UsedBy::Linker,
315                ArgParser::List(list) => {
316                    let Some(l) = list.single() else {
317                        cx.expected_single_argument(list.span);
318                        return;
319                    };
320
321                    match l.meta_item().and_then(|i| i.path().word_sym()) {
322                        Some(sym::compiler) => {
323                            if !cx.features().used_with_arg() {
324                                feature_err(
325                                    &cx.sess(),
326                                    sym::used_with_arg,
327                                    cx.attr_span,
328                                    "`#[used(compiler)]` is currently unstable",
329                                )
330                                .emit();
331                            }
332                            UsedBy::Compiler
333                        }
334                        Some(sym::linker) => {
335                            if !cx.features().used_with_arg() {
336                                feature_err(
337                                    &cx.sess(),
338                                    sym::used_with_arg,
339                                    cx.attr_span,
340                                    "`#[used(linker)]` is currently unstable",
341                                )
342                                .emit();
343                            }
344                            UsedBy::Linker
345                        }
346                        _ => {
347                            cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
348                            return;
349                        }
350                    }
351                }
352                ArgParser::NameValue(_) => return,
353            };
354
355            let target = match used_by {
356                UsedBy::Compiler => &mut group.first_compiler,
357                UsedBy::Linker => &mut group.first_linker,
358            };
359
360            let attr_span = cx.attr_span;
361            if let Some(prev) = *target {
362                cx.warn_unused_duplicate(prev, attr_span);
363            } else {
364                *target = Some(attr_span);
365            }
366        },
367    )];
368    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Static)]);
369
370    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
371        // Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
372        Some(match (self.first_compiler, self.first_linker) {
373            (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
374            (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
375            (None, None) => return None,
376        })
377    }
378}
379
380fn parse_tf_attribute<'c, S: Stage>(
381    cx: &'c mut AcceptContext<'_, '_, S>,
382    args: &'c ArgParser<'_>,
383) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
384    let mut features = Vec::new();
385    let ArgParser::List(list) = args else {
386        cx.expected_list(cx.attr_span);
387        return features;
388    };
389    if list.is_empty() {
390        cx.warn_empty_attribute(cx.attr_span);
391        return features;
392    }
393    for item in list.mixed() {
394        let Some(name_value) = item.meta_item() else {
395            cx.expected_name_value(item.span(), Some(sym::enable));
396            return features;
397        };
398
399        // Validate name
400        let Some(name) = name_value.path().word_sym() else {
401            cx.expected_name_value(name_value.path().span(), Some(sym::enable));
402            return features;
403        };
404        if name != sym::enable {
405            cx.expected_name_value(name_value.path().span(), Some(sym::enable));
406            return features;
407        }
408
409        // Use value
410        let Some(name_value) = name_value.args().name_value() else {
411            cx.expected_name_value(item.span(), Some(sym::enable));
412            return features;
413        };
414        let Some(value_str) = name_value.value_as_str() else {
415            cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
416            return features;
417        };
418        for feature in value_str.as_str().split(",") {
419            features.push((Symbol::intern(feature), item.span()));
420        }
421    }
422    features
423}
424
425pub(crate) struct TargetFeatureParser;
426
427impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
428    type Item = (Symbol, Span);
429    const PATH: &[Symbol] = &[sym::target_feature];
430    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
431        features: items,
432        attr_span: span,
433        was_forced: false,
434    };
435    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
436
437    fn extend<'c>(
438        cx: &'c mut AcceptContext<'_, '_, S>,
439        args: &'c ArgParser<'_>,
440    ) -> impl IntoIterator<Item = Self::Item> + 'c {
441        parse_tf_attribute(cx, args)
442    }
443
444    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
445        Allow(Target::Fn),
446        Allow(Target::Method(MethodKind::Inherent)),
447        Allow(Target::Method(MethodKind::Trait { body: true })),
448        Allow(Target::Method(MethodKind::TraitImpl)),
449        Warn(Target::Statement),
450        Warn(Target::Field),
451        Warn(Target::Arm),
452        Warn(Target::MacroDef),
453    ]);
454}
455
456pub(crate) struct ForceTargetFeatureParser;
457
458impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
459    type Item = (Symbol, Span);
460    const PATH: &[Symbol] = &[sym::force_target_feature];
461    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
462        features: items,
463        attr_span: span,
464        was_forced: true,
465    };
466    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
467
468    fn extend<'c>(
469        cx: &'c mut AcceptContext<'_, '_, S>,
470        args: &'c ArgParser<'_>,
471    ) -> impl IntoIterator<Item = Self::Item> + 'c {
472        parse_tf_attribute(cx, args)
473    }
474
475    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
476        Allow(Target::Fn),
477        Allow(Target::Method(MethodKind::Inherent)),
478        Allow(Target::Method(MethodKind::Trait { body: true })),
479        Allow(Target::Method(MethodKind::TraitImpl)),
480    ]);
481}