rustc_attr_parsing/attributes/
prototype.rs1use rustc_feature::{AttributeTemplate, template};
4use rustc_hir::Target;
5use rustc_hir::attrs::{AttributeKind, MirDialect, MirPhase};
6use rustc_span::{Span, Symbol, sym};
7
8use super::{AttributeOrder, OnDuplicate};
9use crate::attributes::SingleAttributeParser;
10use crate::context::{AcceptContext, AllowedTargets, MaybeWarn, Stage};
11use crate::parser::ArgParser;
12
13pub(crate) struct CustomMirParser;
14
15impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
16 const PATH: &[rustc_span::Symbol] = &[sym::custom_mir];
17
18 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
19
20 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
21
22 const ALLOWED_TARGETS: AllowedTargets =
23 AllowedTargets::AllowList(&[MaybeWarn::Allow(Target::Fn)]);
24
25 const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
26
27 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
28 let Some(list) = args.list() else {
29 cx.expected_list(cx.attr_span);
30 return None;
31 };
32
33 let mut dialect = None;
34 let mut phase = None;
35 let mut failed = false;
36
37 for item in list.mixed() {
38 let Some(meta_item) = item.meta_item() else {
39 cx.expected_name_value(item.span(), None);
40 failed = true;
41 break;
42 };
43
44 if let Some(arg) = meta_item.word_is(sym::dialect) {
45 extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
46 } else if let Some(arg) = meta_item.word_is(sym::phase) {
47 extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
48 } else if let Some(word) = meta_item.path().word() {
49 let word = word.to_string();
50 cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]);
51 failed = true;
52 } else {
53 cx.expected_name_value(meta_item.span(), None);
54 failed = true;
55 };
56 }
57
58 let dialect = parse_dialect(cx, dialect, &mut failed);
59 let phase = parse_phase(cx, phase, &mut failed);
60
61 if failed {
62 return None;
63 }
64
65 Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span))
66 }
67}
68
69fn extract_value<S: Stage>(
70 cx: &mut AcceptContext<'_, '_, S>,
71 key: Symbol,
72 arg: &ArgParser<'_>,
73 span: Span,
74 out_val: &mut Option<(Symbol, Span)>,
75 failed: &mut bool,
76) {
77 if out_val.is_some() {
78 cx.duplicate_key(span, key);
79 *failed = true;
80 return;
81 }
82
83 let Some(val) = arg.name_value() else {
84 cx.expected_single_argument(arg.span().unwrap_or(span));
85 *failed = true;
86 return;
87 };
88
89 let Some(value_sym) = val.value_as_str() else {
90 cx.expected_string_literal(val.value_span, Some(val.value_as_lit()));
91 *failed = true;
92 return;
93 };
94
95 *out_val = Some((value_sym, val.value_span));
96}
97
98fn parse_dialect<S: Stage>(
99 cx: &mut AcceptContext<'_, '_, S>,
100 dialect: Option<(Symbol, Span)>,
101 failed: &mut bool,
102) -> Option<(MirDialect, Span)> {
103 let (dialect, span) = dialect?;
104
105 let dialect = match dialect {
106 sym::analysis => MirDialect::Analysis,
107 sym::built => MirDialect::Built,
108 sym::runtime => MirDialect::Runtime,
109
110 _ => {
111 cx.expected_specific_argument(span, vec!["analysis", "built", "runtime"]);
112 *failed = true;
113 return None;
114 }
115 };
116
117 Some((dialect, span))
118}
119
120fn parse_phase<S: Stage>(
121 cx: &mut AcceptContext<'_, '_, S>,
122 phase: Option<(Symbol, Span)>,
123 failed: &mut bool,
124) -> Option<(MirPhase, Span)> {
125 let (phase, span) = phase?;
126
127 let phase = match phase {
128 sym::initial => MirPhase::Initial,
129 sym::post_cleanup => MirPhase::PostCleanup,
130 sym::optimized => MirPhase::Optimized,
131
132 _ => {
133 cx.expected_specific_argument(span, vec!["initial", "post-cleanup", "optimized"]);
134 *failed = true;
135 return None;
136 }
137 };
138
139 Some((phase, span))
140}