rustc_attr_parsing/attributes/
codegen_attrs.rs

1use rustc_feature::{AttributeTemplate, template};
2use rustc_hir::attrs::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy};
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 kind = match arg.path().word_sym() {
82            Some(sym::off) => CoverageAttrKind::Off,
83            Some(sym::on) => CoverageAttrKind::On,
84            None | Some(_) => {
85                fail_incorrect_argument(arg.span());
86                return None;
87            }
88        };
89
90        Some(AttributeKind::Coverage(cx.attr_span, kind))
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            // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
181            sym::rustc_align,
182            // obviously compatible with self
183            sym::naked,
184            // documentation
185            sym::doc,
186        ];
187
188        let span = self.span?;
189
190        // only if we found a naked attribute do we do the somewhat expensive check
191        'outer: for other_attr in cx.all_attrs {
192            for allowed_attr in ALLOW_LIST {
193                if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
194                    // effectively skips the error message  being emitted below
195                    // if it's a tool attribute
196                    continue 'outer;
197                }
198                if other_attr.word_is(*allowed_attr) {
199                    // effectively skips the error message  being emitted below
200                    // if its an allowed attribute
201                    continue 'outer;
202                }
203
204                if other_attr.word_is(sym::target_feature) {
205                    if !cx.features().naked_functions_target_feature() {
206                        feature_err(
207                            &cx.sess(),
208                            sym::naked_functions_target_feature,
209                            other_attr.span(),
210                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
211                        ).emit();
212                    }
213
214                    continue 'outer;
215                }
216            }
217
218            cx.emit_err(NakedFunctionIncompatibleAttribute {
219                span: other_attr.span(),
220                naked_span: span,
221                attr: other_attr.get_attribute_path().to_string(),
222            });
223        }
224
225        Some(AttributeKind::Naked(span))
226    }
227}
228
229pub(crate) struct TrackCallerParser;
230impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
231    const PATH: &[Symbol] = &[sym::track_caller];
232    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
233    const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
234}
235
236pub(crate) struct NoMangleParser;
237impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
238    const PATH: &[Symbol] = &[sym::no_mangle];
239    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
240    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
241}
242
243#[derive(Default)]
244pub(crate) struct UsedParser {
245    first_compiler: Option<Span>,
246    first_linker: Option<Span>,
247}
248
249// A custom `AttributeParser` is used rather than a Simple attribute parser because
250// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
251// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
252// We can change this to a Simple parser once the warning becomes an error
253impl<S: Stage> AttributeParser<S> for UsedParser {
254    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
255        &[sym::used],
256        template!(Word, List: "compiler|linker"),
257        |group: &mut Self, cx, args| {
258            let used_by = match args {
259                ArgParser::NoArgs => UsedBy::Linker,
260                ArgParser::List(list) => {
261                    let Some(l) = list.single() else {
262                        cx.expected_single_argument(list.span);
263                        return;
264                    };
265
266                    match l.meta_item().and_then(|i| i.path().word_sym()) {
267                        Some(sym::compiler) => {
268                            if !cx.features().used_with_arg() {
269                                feature_err(
270                                    &cx.sess(),
271                                    sym::used_with_arg,
272                                    cx.attr_span,
273                                    "`#[used(compiler)]` is currently unstable",
274                                )
275                                .emit();
276                            }
277                            UsedBy::Compiler
278                        }
279                        Some(sym::linker) => {
280                            if !cx.features().used_with_arg() {
281                                feature_err(
282                                    &cx.sess(),
283                                    sym::used_with_arg,
284                                    cx.attr_span,
285                                    "`#[used(linker)]` is currently unstable",
286                                )
287                                .emit();
288                            }
289                            UsedBy::Linker
290                        }
291                        _ => {
292                            cx.expected_specific_argument(l.span(), vec!["compiler", "linker"]);
293                            return;
294                        }
295                    }
296                }
297                ArgParser::NameValue(_) => return,
298            };
299
300            let target = match used_by {
301                UsedBy::Compiler => &mut group.first_compiler,
302                UsedBy::Linker => &mut group.first_linker,
303            };
304
305            let attr_span = cx.attr_span;
306            if let Some(prev) = *target {
307                cx.warn_unused_duplicate(prev, attr_span);
308            } else {
309                *target = Some(attr_span);
310            }
311        },
312    )];
313
314    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
315        // Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
316        Some(match (self.first_compiler, self.first_linker) {
317            (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
318            (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
319            (None, None) => return None,
320        })
321    }
322}
323
324pub(crate) struct TargetFeatureParser;
325
326impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
327    type Item = (Symbol, Span);
328    const PATH: &[Symbol] = &[sym::target_feature];
329    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
330    const TEMPLATE: AttributeTemplate = template!(List: "enable = \"feat1, feat2\"");
331
332    fn extend<'c>(
333        cx: &'c mut AcceptContext<'_, '_, S>,
334        args: &'c ArgParser<'_>,
335    ) -> impl IntoIterator<Item = Self::Item> + 'c {
336        let mut features = Vec::new();
337        let ArgParser::List(list) = args else {
338            cx.expected_list(cx.attr_span);
339            return features;
340        };
341        if list.is_empty() {
342            cx.warn_empty_attribute(cx.attr_span);
343            return features;
344        }
345        for item in list.mixed() {
346            let Some(name_value) = item.meta_item() else {
347                cx.expected_name_value(item.span(), Some(sym::enable));
348                return features;
349            };
350
351            // Validate name
352            let Some(name) = name_value.path().word_sym() else {
353                cx.expected_name_value(name_value.path().span(), Some(sym::enable));
354                return features;
355            };
356            if name != sym::enable {
357                cx.expected_name_value(name_value.path().span(), Some(sym::enable));
358                return features;
359            }
360
361            // Use value
362            let Some(name_value) = name_value.args().name_value() else {
363                cx.expected_name_value(item.span(), Some(sym::enable));
364                return features;
365            };
366            let Some(value_str) = name_value.value_as_str() else {
367                cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
368                return features;
369            };
370            for feature in value_str.as_str().split(",") {
371                features.push((Symbol::intern(feature), item.span()));
372            }
373        }
374        features
375    }
376}
377
378pub(crate) struct OmitGdbPrettyPrinterSectionParser;
379
380impl<S: Stage> NoArgsAttributeParser<S> for OmitGdbPrettyPrinterSectionParser {
381    const PATH: &[Symbol] = &[sym::omit_gdb_pretty_printer_section];
382    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
383    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::OmitGdbPrettyPrinterSection;
384}