rustc_attr_parsing/attributes/
codegen_attrs.rs

1use rustc_attr_data_structures::{AttributeKind, OptimizeAttr, UsedBy};
2use rustc_feature::{AttributeTemplate, template};
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::KeepLast;
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 ExportNameParser;
56
57impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
58    const PATH: &[rustc_span::Symbol] = &[sym::export_name];
59    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
60    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
61    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
62
63    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
64        let Some(nv) = args.name_value() else {
65            cx.expected_name_value(cx.attr_span, None);
66            return None;
67        };
68        let Some(name) = nv.value_as_str() else {
69            cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
70            return None;
71        };
72        if name.as_str().contains('\0') {
73            // `#[export_name = ...]` will be converted to a null-terminated string,
74            // so it may not contain any null characters.
75            cx.emit_err(NullOnExport { span: cx.attr_span });
76            return None;
77        }
78        Some(AttributeKind::ExportName { name, span: cx.attr_span })
79    }
80}
81
82#[derive(Default)]
83pub(crate) struct NakedParser {
84    span: Option<Span>,
85}
86
87impl<S: Stage> AttributeParser<S> for NakedParser {
88    const ATTRIBUTES: AcceptMapping<Self, S> =
89        &[(&[sym::naked], template!(Word), |this, cx, args| {
90            if let Err(span) = args.no_args() {
91                cx.expected_no_args(span);
92                return;
93            }
94
95            if let Some(earlier) = this.span {
96                let span = cx.attr_span;
97                cx.warn_unused_duplicate(earlier, span);
98            } else {
99                this.span = Some(cx.attr_span);
100            }
101        })];
102
103    fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
104        // FIXME(jdonszelmann): upgrade this list to *parsed* attributes
105        // once all of these have parsed forms. That'd make the check much nicer...
106        //
107        // many attributes don't make sense in combination with #[naked].
108        // Notable attributes that are incompatible with `#[naked]` are:
109        //
110        // * `#[inline]`
111        // * `#[track_caller]`
112        // * `#[test]`, `#[ignore]`, `#[should_panic]`
113        //
114        // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
115        // accurate.
116        const ALLOW_LIST: &[rustc_span::Symbol] = &[
117            // conditional compilation
118            sym::cfg_trace,
119            sym::cfg_attr_trace,
120            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
121            sym::test,
122            sym::ignore,
123            sym::should_panic,
124            sym::bench,
125            // diagnostics
126            sym::allow,
127            sym::warn,
128            sym::deny,
129            sym::forbid,
130            sym::deprecated,
131            sym::must_use,
132            // abi, linking and FFI
133            sym::cold,
134            sym::export_name,
135            sym::link_section,
136            sym::linkage,
137            sym::no_mangle,
138            sym::instruction_set,
139            sym::repr,
140            sym::rustc_std_internal_symbol,
141            sym::align,
142            // obviously compatible with self
143            sym::naked,
144            // documentation
145            sym::doc,
146        ];
147
148        let span = self.span?;
149
150        // only if we found a naked attribute do we do the somewhat expensive check
151        'outer: for other_attr in cx.all_attrs {
152            for allowed_attr in ALLOW_LIST {
153                if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
154                    // effectively skips the error message  being emitted below
155                    // if it's a tool attribute
156                    continue 'outer;
157                }
158                if other_attr.word_is(*allowed_attr) {
159                    // effectively skips the error message  being emitted below
160                    // if its an allowed attribute
161                    continue 'outer;
162                }
163
164                if other_attr.word_is(sym::target_feature) {
165                    if !cx.features().naked_functions_target_feature() {
166                        feature_err(
167                            &cx.sess(),
168                            sym::naked_functions_target_feature,
169                            other_attr.span(),
170                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
171                        ).emit();
172                    }
173
174                    continue 'outer;
175                }
176            }
177
178            cx.emit_err(NakedFunctionIncompatibleAttribute {
179                span: other_attr.span(),
180                naked_span: span,
181                attr: other_attr.get_attribute_path().to_string(),
182            });
183        }
184
185        Some(AttributeKind::Naked(span))
186    }
187}
188
189pub(crate) struct TrackCallerParser;
190impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
191    const PATH: &[Symbol] = &[sym::track_caller];
192    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
193    const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
194}
195
196pub(crate) struct NoMangleParser;
197impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
198    const PATH: &[Symbol] = &[sym::no_mangle];
199    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
200    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
201}
202
203#[derive(Default)]
204pub(crate) struct UsedParser {
205    first_compiler: Option<Span>,
206    first_linker: Option<Span>,
207}
208
209// A custom `AttributeParser` is used rather than a Simple attribute parser because
210// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
211// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
212// We can change this to a Simple parser once the warning becomes an error
213impl<S: Stage> AttributeParser<S> for UsedParser {
214    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
215        &[sym::used],
216        template!(Word, List: "compiler|linker"),
217        |group: &mut Self, cx, args| {
218            let used_by = match args {
219                ArgParser::NoArgs => UsedBy::Linker,
220                ArgParser::List(list) => {
221                    let Some(l) = list.single() else {
222                        cx.expected_single_argument(list.span);
223                        return;
224                    };
225
226                    match l.meta_item().and_then(|i| i.path().word_sym()) {
227                        Some(sym::compiler) => {
228                            if !cx.features().used_with_arg() {
229                                feature_err(
230                                    &cx.sess(),
231                                    sym::used_with_arg,
232                                    cx.attr_span,
233                                    "`#[used(compiler)]` is currently unstable",
234                                )
235                                .emit();
236                            }
237                            UsedBy::Compiler
238                        }
239                        Some(sym::linker) => {
240                            if !cx.features().used_with_arg() {
241                                feature_err(
242                                    &cx.sess(),
243                                    sym::used_with_arg,
244                                    cx.attr_span,
245                                    "`#[used(linker)]` is currently unstable",
246                                )
247                                .emit();
248                            }
249                            UsedBy::Linker
250                        }
251                        _ => {
252                            cx.expected_specific_argument(l.span(), vec!["compiler", "linker"]);
253                            return;
254                        }
255                    }
256                }
257                ArgParser::NameValue(_) => return,
258            };
259
260            let target = match used_by {
261                UsedBy::Compiler => &mut group.first_compiler,
262                UsedBy::Linker => &mut group.first_linker,
263            };
264
265            let attr_span = cx.attr_span;
266            if let Some(prev) = *target {
267                cx.warn_unused_duplicate(prev, attr_span);
268            } else {
269                *target = Some(attr_span);
270            }
271        },
272    )];
273
274    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
275        // Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
276        Some(match (self.first_compiler, self.first_linker) {
277            (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
278            (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
279            (None, None) => return None,
280        })
281    }
282}
283
284pub(crate) struct TargetFeatureParser;
285
286impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
287    type Item = (Symbol, Span);
288    const PATH: &[Symbol] = &[sym::target_feature];
289    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
290    const TEMPLATE: AttributeTemplate = template!(List: "enable = \"feat1, feat2\"");
291
292    fn extend<'c>(
293        cx: &'c mut AcceptContext<'_, '_, S>,
294        args: &'c ArgParser<'_>,
295    ) -> impl IntoIterator<Item = Self::Item> + 'c {
296        let mut features = Vec::new();
297        let ArgParser::List(list) = args else {
298            cx.expected_list(cx.attr_span);
299            return features;
300        };
301        if list.is_empty() {
302            cx.warn_empty_attribute(cx.attr_span);
303            return features;
304        }
305        for item in list.mixed() {
306            let Some(name_value) = item.meta_item() else {
307                cx.expected_name_value(item.span(), Some(sym::enable));
308                return features;
309            };
310
311            // Validate name
312            let Some(name) = name_value.path().word_sym() else {
313                cx.expected_name_value(name_value.path().span(), Some(sym::enable));
314                return features;
315            };
316            if name != sym::enable {
317                cx.expected_name_value(name_value.path().span(), Some(sym::enable));
318                return features;
319            }
320
321            // Use value
322            let Some(name_value) = name_value.args().name_value() else {
323                cx.expected_name_value(item.span(), Some(sym::enable));
324                return features;
325            };
326            let Some(value_str) = name_value.value_as_str() else {
327                cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
328                return features;
329            };
330            for feature in value_str.as_str().split(",") {
331                features.push((Symbol::intern(feature), item.span()));
332            }
333        }
334        features
335    }
336}