1use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy};
2use rustc_session::parse::feature_err;
3
4use super::prelude::*;
5use crate::session_diagnostics::{
6 NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,
7 ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
8};
9use crate::target_checking::Policy::AllowSilent;
10
11pub(crate) struct OptimizeParser;
12
13impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
14 const PATH: &[Symbol] = &[sym::optimize];
15 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
16 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
17 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
18 Allow(Target::Fn),
19 Allow(Target::Closure),
20 Allow(Target::Method(MethodKind::Trait { body: true })),
21 Allow(Target::Method(MethodKind::TraitImpl)),
22 Allow(Target::Method(MethodKind::Inherent)),
23 ]);
24 const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);
25
26 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
27 let Some(list) = args.list() else {
28 cx.expected_list(cx.attr_span);
29 return None;
30 };
31
32 let Some(single) = list.single() else {
33 cx.expected_single_argument(list.span);
34 return None;
35 };
36
37 let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
38 Some(sym::size) => OptimizeAttr::Size,
39 Some(sym::speed) => OptimizeAttr::Speed,
40 Some(sym::none) => OptimizeAttr::DoNotOptimize,
41 _ => {
42 cx.expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
43 OptimizeAttr::Default
44 }
45 };
46
47 Some(AttributeKind::Optimize(res, cx.attr_span))
48 }
49}
50
51pub(crate) struct ColdParser;
52
53impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
54 const PATH: &[Symbol] = &[sym::cold];
55 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
56 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
57 Allow(Target::Fn),
58 Allow(Target::Method(MethodKind::Trait { body: true })),
59 Allow(Target::Method(MethodKind::TraitImpl)),
60 Allow(Target::Method(MethodKind::Trait { body: false })),
61 Allow(Target::Method(MethodKind::Inherent)),
62 Allow(Target::ForeignFn),
63 Allow(Target::Closure),
64 ]);
65 const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
66}
67
68pub(crate) struct CoverageParser;
69
70impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
71 const PATH: &[Symbol] = &[sym::coverage];
72 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
73 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
74 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
75 Allow(Target::Fn),
76 Allow(Target::Closure),
77 Allow(Target::Method(MethodKind::Trait { body: true })),
78 Allow(Target::Method(MethodKind::TraitImpl)),
79 Allow(Target::Method(MethodKind::Inherent)),
80 Allow(Target::Impl { of_trait: true }),
81 Allow(Target::Impl { of_trait: false }),
82 Allow(Target::Mod),
83 Allow(Target::Crate),
84 ]);
85 const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
86
87 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
88 let Some(args) = args.list() else {
89 cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]);
90 return None;
91 };
92
93 let Some(arg) = args.single() else {
94 cx.expected_single_argument(args.span);
95 return None;
96 };
97
98 let fail_incorrect_argument =
99 |span| cx.expected_specific_argument(span, &[sym::on, sym::off]);
100
101 let Some(arg) = arg.meta_item() else {
102 fail_incorrect_argument(args.span);
103 return None;
104 };
105
106 let kind = match arg.path().word_sym() {
107 Some(sym::off) => CoverageAttrKind::Off,
108 Some(sym::on) => CoverageAttrKind::On,
109 None | Some(_) => {
110 fail_incorrect_argument(arg.span());
111 return None;
112 }
113 };
114
115 Some(AttributeKind::Coverage(cx.attr_span, kind))
116 }
117}
118
119pub(crate) struct ExportNameParser;
120
121impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
122 const PATH: &[rustc_span::Symbol] = &[sym::export_name];
123 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
124 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
125 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
126 Allow(Target::Static),
127 Allow(Target::Fn),
128 Allow(Target::Method(MethodKind::Inherent)),
129 Allow(Target::Method(MethodKind::Trait { body: true })),
130 Allow(Target::Method(MethodKind::TraitImpl)),
131 Warn(Target::Field),
132 Warn(Target::Arm),
133 Warn(Target::MacroDef),
134 Warn(Target::MacroCall),
135 ]);
136 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
137
138 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
139 let Some(nv) = args.name_value() else {
140 cx.expected_name_value(cx.attr_span, None);
141 return None;
142 };
143 let Some(name) = nv.value_as_str() else {
144 cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
145 return None;
146 };
147 if name.as_str().contains('\0') {
148 cx.emit_err(NullOnExport { span: cx.attr_span });
151 return None;
152 }
153 Some(AttributeKind::ExportName { name, span: cx.attr_span })
154 }
155}
156
157pub(crate) struct ObjcClassParser;
158
159impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
160 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
161 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
162 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
163 const ALLOWED_TARGETS: AllowedTargets =
164 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
165 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");
166
167 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
168 let Some(nv) = args.name_value() else {
169 cx.expected_name_value(cx.attr_span, None);
170 return None;
171 };
172 let Some(classname) = nv.value_as_str() else {
173 cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
177 return None;
178 };
179 if classname.as_str().contains('\0') {
180 cx.emit_err(NullOnObjcClass { span: nv.value_span });
183 return None;
184 }
185 Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
186 }
187}
188
189pub(crate) struct ObjcSelectorParser;
190
191impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
192 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
193 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
194 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
195 const ALLOWED_TARGETS: AllowedTargets =
196 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
197 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");
198
199 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
200 let Some(nv) = args.name_value() else {
201 cx.expected_name_value(cx.attr_span, None);
202 return None;
203 };
204 let Some(methname) = nv.value_as_str() else {
205 cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
209 return None;
210 };
211 if methname.as_str().contains('\0') {
212 cx.emit_err(NullOnObjcSelector { span: nv.value_span });
215 return None;
216 }
217 Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
218 }
219}
220
221#[derive(Default)]
222pub(crate) struct NakedParser {
223 span: Option<Span>,
224}
225
226impl<S: Stage> AttributeParser<S> for NakedParser {
227 const ATTRIBUTES: AcceptMapping<Self, S> =
228 &[(&[sym::naked], template!(Word), |this, cx, args| {
229 if let Err(span) = args.no_args() {
230 cx.expected_no_args(span);
231 return;
232 }
233
234 if let Some(earlier) = this.span {
235 let span = cx.attr_span;
236 cx.warn_unused_duplicate(earlier, span);
237 } else {
238 this.span = Some(cx.attr_span);
239 }
240 })];
241 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
242 Allow(Target::Fn),
243 Allow(Target::Method(MethodKind::Inherent)),
244 Allow(Target::Method(MethodKind::Trait { body: true })),
245 Allow(Target::Method(MethodKind::TraitImpl)),
246 Warn(Target::MacroCall),
247 ]);
248
249 fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
250 const ALLOW_LIST: &[rustc_span::Symbol] = &[
263 sym::cfg_trace,
265 sym::cfg_attr_trace,
266 sym::test,
268 sym::ignore,
269 sym::should_panic,
270 sym::bench,
271 sym::allow,
273 sym::warn,
274 sym::deny,
275 sym::forbid,
276 sym::deprecated,
277 sym::must_use,
278 sym::cold,
280 sym::export_name,
281 sym::link_section,
282 sym::linkage,
283 sym::no_mangle,
284 sym::instruction_set,
285 sym::repr,
286 sym::rustc_std_internal_symbol,
287 sym::rustc_align,
289 sym::rustc_align_static,
290 sym::naked,
292 sym::doc,
294 ];
295
296 let span = self.span?;
297
298 'outer: for other_attr in cx.all_attrs {
300 for allowed_attr in ALLOW_LIST {
301 if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
302 continue 'outer;
305 }
306 if other_attr.word_is(*allowed_attr) {
307 continue 'outer;
310 }
311
312 if other_attr.word_is(sym::target_feature) {
313 if !cx.features().naked_functions_target_feature() {
314 feature_err(
315 &cx.sess(),
316 sym::naked_functions_target_feature,
317 other_attr.span(),
318 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
319 ).emit();
320 }
321
322 continue 'outer;
323 }
324 }
325
326 cx.emit_err(NakedFunctionIncompatibleAttribute {
327 span: other_attr.span(),
328 naked_span: span,
329 attr: other_attr.get_attribute_path().to_string(),
330 });
331 }
332
333 Some(AttributeKind::Naked(span))
334 }
335}
336
337pub(crate) struct TrackCallerParser;
338impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
339 const PATH: &[Symbol] = &[sym::track_caller];
340 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
341 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
342 Allow(Target::Fn),
343 Allow(Target::Method(MethodKind::Inherent)),
344 Allow(Target::Method(MethodKind::Trait { body: true })),
345 Allow(Target::Method(MethodKind::TraitImpl)),
346 Allow(Target::Method(MethodKind::Trait { body: false })),
347 Allow(Target::ForeignFn),
348 Allow(Target::Closure),
349 Warn(Target::MacroDef),
350 Warn(Target::Arm),
351 Warn(Target::Field),
352 Warn(Target::MacroCall),
353 ]);
354 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
355}
356
357pub(crate) struct NoMangleParser;
358impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
359 const PATH: &[Symbol] = &[sym::no_mangle];
360 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
361 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
362 Allow(Target::Fn),
363 Allow(Target::Static),
364 Allow(Target::Method(MethodKind::Inherent)),
365 Allow(Target::Method(MethodKind::TraitImpl)),
366 AllowSilent(Target::Const), Error(Target::Closure),
368 ]);
369 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
370}
371
372#[derive(Default)]
373pub(crate) struct UsedParser {
374 first_compiler: Option<Span>,
375 first_linker: Option<Span>,
376 first_default: Option<Span>,
377}
378
379impl<S: Stage> AttributeParser<S> for UsedParser {
384 const ATTRIBUTES: AcceptMapping<Self, S> = &[(
385 &[sym::used],
386 template!(Word, List: &["compiler", "linker"]),
387 |group: &mut Self, cx, args| {
388 let used_by = match args {
389 ArgParser::NoArgs => UsedBy::Default,
390 ArgParser::List(list) => {
391 let Some(l) = list.single() else {
392 cx.expected_single_argument(list.span);
393 return;
394 };
395
396 match l.meta_item().and_then(|i| i.path().word_sym()) {
397 Some(sym::compiler) => {
398 if !cx.features().used_with_arg() {
399 feature_err(
400 &cx.sess(),
401 sym::used_with_arg,
402 cx.attr_span,
403 "`#[used(compiler)]` is currently unstable",
404 )
405 .emit();
406 }
407 UsedBy::Compiler
408 }
409 Some(sym::linker) => {
410 if !cx.features().used_with_arg() {
411 feature_err(
412 &cx.sess(),
413 sym::used_with_arg,
414 cx.attr_span,
415 "`#[used(linker)]` is currently unstable",
416 )
417 .emit();
418 }
419 UsedBy::Linker
420 }
421 _ => {
422 cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
423 return;
424 }
425 }
426 }
427 ArgParser::NameValue(_) => return,
428 };
429
430 let attr_span = cx.attr_span;
431
432 let target = match used_by {
436 UsedBy::Compiler => &mut group.first_compiler,
437 UsedBy::Linker => {
438 if let Some(prev) = group.first_default {
439 cx.warn_unused_duplicate(prev, attr_span);
440 return;
441 }
442 &mut group.first_linker
443 }
444 UsedBy::Default => {
445 if let Some(prev) = group.first_linker {
446 cx.warn_unused_duplicate(prev, attr_span);
447 return;
448 }
449 &mut group.first_default
450 }
451 };
452
453 if let Some(prev) = *target {
454 cx.warn_unused_duplicate(prev, attr_span);
455 } else {
456 *target = Some(attr_span);
457 }
458 },
459 )];
460 const ALLOWED_TARGETS: AllowedTargets =
461 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
462
463 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
464 Some(match (self.first_compiler, self.first_linker, self.first_default) {
467 (_, Some(span), _) => AttributeKind::Used { used_by: UsedBy::Linker, span },
468 (Some(span), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
469 (_, _, Some(span)) => AttributeKind::Used { used_by: UsedBy::Default, span },
470 (None, None, None) => return None,
471 })
472 }
473}
474
475fn parse_tf_attribute<'c, S: Stage>(
476 cx: &'c mut AcceptContext<'_, '_, S>,
477 args: &'c ArgParser<'_>,
478) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
479 let mut features = Vec::new();
480 let ArgParser::List(list) = args else {
481 cx.expected_list(cx.attr_span);
482 return features;
483 };
484 if list.is_empty() {
485 cx.warn_empty_attribute(cx.attr_span);
486 return features;
487 }
488 for item in list.mixed() {
489 let Some(name_value) = item.meta_item() else {
490 cx.expected_name_value(item.span(), Some(sym::enable));
491 return features;
492 };
493
494 let Some(name) = name_value.path().word_sym() else {
496 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
497 return features;
498 };
499 if name != sym::enable {
500 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
501 return features;
502 }
503
504 let Some(name_value) = name_value.args().name_value() else {
506 cx.expected_name_value(item.span(), Some(sym::enable));
507 return features;
508 };
509 let Some(value_str) = name_value.value_as_str() else {
510 cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
511 return features;
512 };
513 for feature in value_str.as_str().split(",") {
514 features.push((Symbol::intern(feature), item.span()));
515 }
516 }
517 features
518}
519
520pub(crate) struct TargetFeatureParser;
521
522impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
523 type Item = (Symbol, Span);
524 const PATH: &[Symbol] = &[sym::target_feature];
525 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
526 features: items,
527 attr_span: span,
528 was_forced: false,
529 };
530 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
531
532 fn extend<'c>(
533 cx: &'c mut AcceptContext<'_, '_, S>,
534 args: &'c ArgParser<'_>,
535 ) -> impl IntoIterator<Item = Self::Item> + 'c {
536 parse_tf_attribute(cx, args)
537 }
538
539 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
540 Allow(Target::Fn),
541 Allow(Target::Method(MethodKind::Inherent)),
542 Allow(Target::Method(MethodKind::Trait { body: true })),
543 Allow(Target::Method(MethodKind::TraitImpl)),
544 Warn(Target::Statement),
545 Warn(Target::Field),
546 Warn(Target::Arm),
547 Warn(Target::MacroDef),
548 Warn(Target::MacroCall),
549 ]);
550}
551
552pub(crate) struct ForceTargetFeatureParser;
553
554impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
555 type Item = (Symbol, Span);
556 const PATH: &[Symbol] = &[sym::force_target_feature];
557 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
558 features: items,
559 attr_span: span,
560 was_forced: true,
561 };
562 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
563 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
564 Allow(Target::Fn),
565 Allow(Target::Method(MethodKind::Inherent)),
566 Allow(Target::Method(MethodKind::Trait { body: true })),
567 Allow(Target::Method(MethodKind::TraitImpl)),
568 ]);
569
570 fn extend<'c>(
571 cx: &'c mut AcceptContext<'_, '_, S>,
572 args: &'c ArgParser<'_>,
573 ) -> impl IntoIterator<Item = Self::Item> + 'c {
574 parse_tf_attribute(cx, args)
575 }
576}
577
578pub(crate) struct SanitizeParser;
579
580impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
581 const PATH: &[Symbol] = &[sym::sanitize];
582
583 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
585
586 const TEMPLATE: AttributeTemplate = template!(List: &[
587 r#"address = "on|off""#,
588 r#"kernel_address = "on|off""#,
589 r#"cfi = "on|off""#,
590 r#"hwaddress = "on|off""#,
591 r#"kcfi = "on|off""#,
592 r#"memory = "on|off""#,
593 r#"memtag = "on|off""#,
594 r#"shadow_call_stack = "on|off""#,
595 r#"thread = "on|off""#
596 ]);
597
598 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
599 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
600
601 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
602 let Some(list) = args.list() else {
603 cx.expected_list(cx.attr_span);
604 return None;
605 };
606
607 let mut on_set = SanitizerSet::empty();
608 let mut off_set = SanitizerSet::empty();
609
610 for item in list.mixed() {
611 let Some(item) = item.meta_item() else {
612 cx.expected_name_value(item.span(), None);
613 continue;
614 };
615
616 let path = item.path().word_sym();
617 let Some(value) = item.args().name_value() else {
618 cx.expected_name_value(item.span(), path);
619 continue;
620 };
621
622 let mut apply = |s: SanitizerSet| {
623 let is_on = match value.value_as_str() {
624 Some(sym::on) => true,
625 Some(sym::off) => false,
626 Some(_) => {
627 cx.expected_specific_argument_strings(
628 value.value_span,
629 &[sym::on, sym::off],
630 );
631 return;
632 }
633 None => {
634 cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
635 return;
636 }
637 };
638
639 if is_on {
640 on_set |= s;
641 } else {
642 off_set |= s;
643 }
644 };
645
646 match path {
647 Some(sym::address) | Some(sym::kernel_address) => {
648 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
649 }
650 Some(sym::cfi) => apply(SanitizerSet::CFI),
651 Some(sym::kcfi) => apply(SanitizerSet::KCFI),
652 Some(sym::memory) => apply(SanitizerSet::MEMORY),
653 Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
654 Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
655 Some(sym::thread) => apply(SanitizerSet::THREAD),
656 Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
657 _ => {
658 cx.expected_specific_argument_strings(
659 item.path().span(),
660 &[
661 sym::address,
662 sym::cfi,
663 sym::kcfi,
664 sym::memory,
665 sym::memtag,
666 sym::shadow_call_stack,
667 sym::thread,
668 sym::hwaddress,
669 ],
670 );
671 continue;
672 }
673 }
674 }
675
676 Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
677 }
678}