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 crate::attributes::SingleAttributeParser;
9use crate::context::AcceptContext;
10use crate::parser::ArgParser;
11use crate::session_diagnostics;
12use crate::target_checking::AllowedTargets;
13use crate::target_checking::Policy::Allow;
14
15pub(crate) struct CustomMirParser;
16
17impl SingleAttributeParser for CustomMirParser {
18 const PATH: &[rustc_span::Symbol] = &[sym::custom_mir];
19
20 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
21
22 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&[r#"dialect = "...", phase = "...""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"dialect = "...", phase = "...""#]);
23
24 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
25 let list = cx.expect_list(args, cx.attr_span)?;
26
27 let mut dialect = None;
28 let mut phase = None;
29 let mut failed = false;
30
31 for item in list.mixed() {
32 let Some(meta_item) = item.meta_item() else {
33 cx.adcx().expected_name_value(item.span(), None);
34 failed = true;
35 break;
36 };
37
38 if let Some(arg) = meta_item.word_is(sym::dialect) {
39 extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
40 } else if let Some(arg) = meta_item.word_is(sym::phase) {
41 extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
42 } else if let Some(..) = meta_item.path().word() {
43 cx.adcx().expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]);
44 failed = true;
45 } else {
46 cx.adcx().expected_name_value(meta_item.span(), None);
47 failed = true;
48 };
49 }
50
51 let dialect = parse_dialect(cx, dialect, &mut failed);
52 let phase = parse_phase(cx, phase, &mut failed);
53 check_custom_mir(cx, dialect, phase, &mut failed);
54
55 if failed {
56 return None;
57 }
58
59 Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span))
60 }
61}
62
63fn extract_value(
64 cx: &mut AcceptContext<'_, '_>,
65 key: Symbol,
66 arg: &ArgParser,
67 span: Span,
68 out_val: &mut Option<(Symbol, Span)>,
69 failed: &mut bool,
70) {
71 if out_val.is_some() {
72 cx.adcx().duplicate_key(span, key);
73 *failed = true;
74 return;
75 }
76
77 let Some(val) = arg.name_value() else {
78 cx.adcx().expected_name_value(span, Some(key));
79 *failed = true;
80 return;
81 };
82
83 let Some(value_sym) = val.value_as_str() else {
84 cx.adcx().expected_string_literal(val.value_span, Some(val.value_as_lit()));
85 *failed = true;
86 return;
87 };
88
89 *out_val = Some((value_sym, val.value_span));
90}
91
92fn parse_dialect(
93 cx: &mut AcceptContext<'_, '_>,
94 dialect: Option<(Symbol, Span)>,
95 failed: &mut bool,
96) -> Option<(MirDialect, Span)> {
97 let (dialect, span) = dialect?;
98
99 let dialect = match dialect {
100 sym::analysis => MirDialect::Analysis,
101 sym::built => MirDialect::Built,
102 sym::runtime => MirDialect::Runtime,
103
104 _ => {
105 cx.adcx().expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]);
106 *failed = true;
107 return None;
108 }
109 };
110
111 Some((dialect, span))
112}
113
114fn parse_phase(
115 cx: &mut AcceptContext<'_, '_>,
116 phase: Option<(Symbol, Span)>,
117 failed: &mut bool,
118) -> Option<(MirPhase, Span)> {
119 let (phase, span) = phase?;
120
121 let phase = match phase {
122 sym::initial => MirPhase::Initial,
123 sym::post_cleanup => MirPhase::PostCleanup,
124 sym::optimized => MirPhase::Optimized,
125
126 _ => {
127 cx.adcx().expected_specific_argument(
128 span,
129 &[sym::initial, sym::post_cleanup, sym::optimized],
130 );
131 *failed = true;
132 return None;
133 }
134 };
135
136 Some((phase, span))
137}
138
139fn check_custom_mir(
140 cx: &mut AcceptContext<'_, '_>,
141 dialect: Option<(MirDialect, Span)>,
142 phase: Option<(MirPhase, Span)>,
143 failed: &mut bool,
144) {
145 let attr_span = cx.attr_span;
146 let Some((dialect, dialect_span)) = dialect else {
147 if let Some((_, phase_span)) = phase {
148 *failed = true;
149 cx.emit_err(session_diagnostics::CustomMirPhaseRequiresDialect {
150 attr_span,
151 phase_span,
152 });
153 }
154 return;
155 };
156
157 match dialect {
158 MirDialect::Analysis => {
159 if let Some((MirPhase::Optimized, phase_span)) = phase {
160 *failed = true;
161 cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase {
162 dialect,
163 phase: MirPhase::Optimized,
164 attr_span,
165 dialect_span,
166 phase_span,
167 });
168 }
169 }
170
171 MirDialect::Built => {
172 if let Some((phase, phase_span)) = phase {
173 *failed = true;
174 cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase {
175 dialect,
176 phase,
177 attr_span,
178 dialect_span,
179 phase_span,
180 });
181 }
182 }
183 MirDialect::Runtime => {}
184 }
185}