rustc_attr_parsing/attributes/
codegen_attrs.rs1use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, 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 Warn(Target::MacroCall),
131 ]);
132 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
133
134 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
135 let Some(nv) = args.name_value() else {
136 cx.expected_name_value(cx.attr_span, None);
137 return None;
138 };
139 let Some(name) = nv.value_as_str() else {
140 cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
141 return None;
142 };
143 if name.as_str().contains('\0') {
144 cx.emit_err(NullOnExport { span: cx.attr_span });
147 return None;
148 }
149 Some(AttributeKind::ExportName { name, span: cx.attr_span })
150 }
151}
152
153#[derive(Default)]
154pub(crate) struct NakedParser {
155 span: Option<Span>,
156}
157
158impl<S: Stage> AttributeParser<S> for NakedParser {
159 const ATTRIBUTES: AcceptMapping<Self, S> =
160 &[(&[sym::naked], template!(Word), |this, cx, args| {
161 if let Err(span) = args.no_args() {
162 cx.expected_no_args(span);
163 return;
164 }
165
166 if let Some(earlier) = this.span {
167 let span = cx.attr_span;
168 cx.warn_unused_duplicate(earlier, span);
169 } else {
170 this.span = Some(cx.attr_span);
171 }
172 })];
173 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
174 Allow(Target::Fn),
175 Allow(Target::Method(MethodKind::Inherent)),
176 Allow(Target::Method(MethodKind::Trait { body: true })),
177 Allow(Target::Method(MethodKind::TraitImpl)),
178 Warn(Target::MacroCall),
179 ]);
180
181 fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
182 const ALLOW_LIST: &[rustc_span::Symbol] = &[
195 sym::cfg_trace,
197 sym::cfg_attr_trace,
198 sym::test,
200 sym::ignore,
201 sym::should_panic,
202 sym::bench,
203 sym::allow,
205 sym::warn,
206 sym::deny,
207 sym::forbid,
208 sym::deprecated,
209 sym::must_use,
210 sym::cold,
212 sym::export_name,
213 sym::link_section,
214 sym::linkage,
215 sym::no_mangle,
216 sym::instruction_set,
217 sym::repr,
218 sym::rustc_std_internal_symbol,
219 sym::rustc_align,
221 sym::rustc_align_static,
222 sym::naked,
224 sym::doc,
226 ];
227
228 let span = self.span?;
229
230 'outer: for other_attr in cx.all_attrs {
232 for allowed_attr in ALLOW_LIST {
233 if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
234 continue 'outer;
237 }
238 if other_attr.word_is(*allowed_attr) {
239 continue 'outer;
242 }
243
244 if other_attr.word_is(sym::target_feature) {
245 if !cx.features().naked_functions_target_feature() {
246 feature_err(
247 &cx.sess(),
248 sym::naked_functions_target_feature,
249 other_attr.span(),
250 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
251 ).emit();
252 }
253
254 continue 'outer;
255 }
256 }
257
258 cx.emit_err(NakedFunctionIncompatibleAttribute {
259 span: other_attr.span(),
260 naked_span: span,
261 attr: other_attr.get_attribute_path().to_string(),
262 });
263 }
264
265 Some(AttributeKind::Naked(span))
266 }
267}
268
269pub(crate) struct TrackCallerParser;
270impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
271 const PATH: &[Symbol] = &[sym::track_caller];
272 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
273 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
274 Allow(Target::Fn),
275 Allow(Target::Method(MethodKind::Inherent)),
276 Allow(Target::Method(MethodKind::Trait { body: true })),
277 Allow(Target::Method(MethodKind::TraitImpl)),
278 Allow(Target::Method(MethodKind::Trait { body: false })),
279 Allow(Target::ForeignFn),
280 Allow(Target::Closure),
281 Warn(Target::MacroDef),
282 Warn(Target::Arm),
283 Warn(Target::Field),
284 Warn(Target::MacroCall),
285 ]);
286 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
287}
288
289pub(crate) struct NoMangleParser;
290impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
291 const PATH: &[Symbol] = &[sym::no_mangle];
292 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
293 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
294 Allow(Target::Fn),
295 Allow(Target::Static),
296 Allow(Target::Method(MethodKind::Inherent)),
297 Allow(Target::Method(MethodKind::TraitImpl)),
298 ]);
299 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
300}
301
302#[derive(Default)]
303pub(crate) struct UsedParser {
304 first_compiler: Option<Span>,
305 first_linker: Option<Span>,
306}
307
308impl<S: Stage> AttributeParser<S> for UsedParser {
313 const ATTRIBUTES: AcceptMapping<Self, S> = &[(
314 &[sym::used],
315 template!(Word, List: &["compiler", "linker"]),
316 |group: &mut Self, cx, args| {
317 let used_by = match args {
318 ArgParser::NoArgs => UsedBy::Linker,
319 ArgParser::List(list) => {
320 let Some(l) = list.single() else {
321 cx.expected_single_argument(list.span);
322 return;
323 };
324
325 match l.meta_item().and_then(|i| i.path().word_sym()) {
326 Some(sym::compiler) => {
327 if !cx.features().used_with_arg() {
328 feature_err(
329 &cx.sess(),
330 sym::used_with_arg,
331 cx.attr_span,
332 "`#[used(compiler)]` is currently unstable",
333 )
334 .emit();
335 }
336 UsedBy::Compiler
337 }
338 Some(sym::linker) => {
339 if !cx.features().used_with_arg() {
340 feature_err(
341 &cx.sess(),
342 sym::used_with_arg,
343 cx.attr_span,
344 "`#[used(linker)]` is currently unstable",
345 )
346 .emit();
347 }
348 UsedBy::Linker
349 }
350 _ => {
351 cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
352 return;
353 }
354 }
355 }
356 ArgParser::NameValue(_) => return,
357 };
358
359 let target = match used_by {
360 UsedBy::Compiler => &mut group.first_compiler,
361 UsedBy::Linker => &mut group.first_linker,
362 };
363
364 let attr_span = cx.attr_span;
365 if let Some(prev) = *target {
366 cx.warn_unused_duplicate(prev, attr_span);
367 } else {
368 *target = Some(attr_span);
369 }
370 },
371 )];
372 const ALLOWED_TARGETS: AllowedTargets =
373 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
374
375 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
376 Some(match (self.first_compiler, self.first_linker) {
378 (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
379 (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
380 (None, None) => return None,
381 })
382 }
383}
384
385fn parse_tf_attribute<'c, S: Stage>(
386 cx: &'c mut AcceptContext<'_, '_, S>,
387 args: &'c ArgParser<'_>,
388) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
389 let mut features = Vec::new();
390 let ArgParser::List(list) = args else {
391 cx.expected_list(cx.attr_span);
392 return features;
393 };
394 if list.is_empty() {
395 cx.warn_empty_attribute(cx.attr_span);
396 return features;
397 }
398 for item in list.mixed() {
399 let Some(name_value) = item.meta_item() else {
400 cx.expected_name_value(item.span(), Some(sym::enable));
401 return features;
402 };
403
404 let Some(name) = name_value.path().word_sym() else {
406 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
407 return features;
408 };
409 if name != sym::enable {
410 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
411 return features;
412 }
413
414 let Some(name_value) = name_value.args().name_value() else {
416 cx.expected_name_value(item.span(), Some(sym::enable));
417 return features;
418 };
419 let Some(value_str) = name_value.value_as_str() else {
420 cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
421 return features;
422 };
423 for feature in value_str.as_str().split(",") {
424 features.push((Symbol::intern(feature), item.span()));
425 }
426 }
427 features
428}
429
430pub(crate) struct TargetFeatureParser;
431
432impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
433 type Item = (Symbol, Span);
434 const PATH: &[Symbol] = &[sym::target_feature];
435 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
436 features: items,
437 attr_span: span,
438 was_forced: false,
439 };
440 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
441
442 fn extend<'c>(
443 cx: &'c mut AcceptContext<'_, '_, S>,
444 args: &'c ArgParser<'_>,
445 ) -> impl IntoIterator<Item = Self::Item> + 'c {
446 parse_tf_attribute(cx, args)
447 }
448
449 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
450 Allow(Target::Fn),
451 Allow(Target::Method(MethodKind::Inherent)),
452 Allow(Target::Method(MethodKind::Trait { body: true })),
453 Allow(Target::Method(MethodKind::TraitImpl)),
454 Warn(Target::Statement),
455 Warn(Target::Field),
456 Warn(Target::Arm),
457 Warn(Target::MacroDef),
458 Warn(Target::MacroCall),
459 ]);
460}
461
462pub(crate) struct ForceTargetFeatureParser;
463
464impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
465 type Item = (Symbol, Span);
466 const PATH: &[Symbol] = &[sym::force_target_feature];
467 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
468 features: items,
469 attr_span: span,
470 was_forced: true,
471 };
472 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
473 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
474 Allow(Target::Fn),
475 Allow(Target::Method(MethodKind::Inherent)),
476 Allow(Target::Method(MethodKind::Trait { body: true })),
477 Allow(Target::Method(MethodKind::TraitImpl)),
478 ]);
479
480 fn extend<'c>(
481 cx: &'c mut AcceptContext<'_, '_, S>,
482 args: &'c ArgParser<'_>,
483 ) -> impl IntoIterator<Item = Self::Item> + 'c {
484 parse_tf_attribute(cx, args)
485 }
486}
487
488pub(crate) struct SanitizeParser;
489
490impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
491 const PATH: &[Symbol] = &[sym::sanitize];
492
493 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
495
496 const TEMPLATE: AttributeTemplate = template!(List: &[
497 r#"address = "on|off""#,
498 r#"kernel_address = "on|off""#,
499 r#"cfi = "on|off""#,
500 r#"hwaddress = "on|off""#,
501 r#"kcfi = "on|off""#,
502 r#"memory = "on|off""#,
503 r#"memtag = "on|off""#,
504 r#"shadow_call_stack = "on|off""#,
505 r#"thread = "on|off""#
506 ]);
507
508 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
509 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
510
511 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
512 let Some(list) = args.list() else {
513 cx.expected_list(cx.attr_span);
514 return None;
515 };
516
517 let mut on_set = SanitizerSet::empty();
518 let mut off_set = SanitizerSet::empty();
519
520 for item in list.mixed() {
521 let Some(item) = item.meta_item() else {
522 cx.expected_name_value(item.span(), None);
523 continue;
524 };
525
526 let path = item.path().word_sym();
527 let Some(value) = item.args().name_value() else {
528 cx.expected_name_value(item.span(), path);
529 continue;
530 };
531
532 let mut apply = |s: SanitizerSet| {
533 let is_on = match value.value_as_str() {
534 Some(sym::on) => true,
535 Some(sym::off) => false,
536 Some(_) => {
537 cx.expected_specific_argument_strings(
538 value.value_span,
539 &[sym::on, sym::off],
540 );
541 return;
542 }
543 None => {
544 cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
545 return;
546 }
547 };
548
549 if is_on {
550 on_set |= s;
551 } else {
552 off_set |= s;
553 }
554 };
555
556 match path {
557 Some(sym::address) | Some(sym::kernel_address) => {
558 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
559 }
560 Some(sym::cfi) => apply(SanitizerSet::CFI),
561 Some(sym::kcfi) => apply(SanitizerSet::KCFI),
562 Some(sym::memory) => apply(SanitizerSet::MEMORY),
563 Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
564 Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
565 Some(sym::thread) => apply(SanitizerSet::THREAD),
566 Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
567 _ => {
568 cx.expected_specific_argument_strings(
569 item.path().span(),
570 &[
571 sym::address,
572 sym::cfi,
573 sym::kcfi,
574 sym::memory,
575 sym::memtag,
576 sym::shadow_call_stack,
577 sym::thread,
578 sym::hwaddress,
579 ],
580 );
581 continue;
582 }
583 }
584 }
585
586 Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
587 }
588}