rustc_attr_parsing/attributes/
codegen_attrs.rs1use 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 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 const ALLOW_LIST: &[rustc_span::Symbol] = &[
201 sym::cfg_trace,
203 sym::cfg_attr_trace,
204 sym::test,
206 sym::ignore,
207 sym::should_panic,
208 sym::bench,
209 sym::allow,
211 sym::warn,
212 sym::deny,
213 sym::forbid,
214 sym::deprecated,
215 sym::must_use,
216 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 sym::rustc_align,
227 sym::naked,
229 sym::doc,
231 ];
232
233 let span = self.span?;
234
235 '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 continue 'outer;
242 }
243 if other_attr.word_is(*allowed_attr) {
244 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
312impl<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 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 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 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}