rustc_attr_parsing/attributes/
codegen_attrs.rs

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