1use rustc_ast::{self as ast, AsmMacro};
2use rustc_span::{Span, Symbol, kw};
3
4use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos};
5use crate::{PResult, Parser, errors, exp, token};
6
7pub struct AsmArg {
10 pub kind: AsmArgKind,
11 pub attributes: AsmAttrVec,
12 pub span: Span,
13}
14
15pub enum AsmArgKind {
16 Template(Box<ast::Expr>),
17 Operand(Option<Symbol>, ast::InlineAsmOperand),
18 Options(Vec<AsmOption>),
19 ClobberAbi(Vec<(Symbol, Span)>),
20}
21
22pub struct AsmOption {
23 pub symbol: Symbol,
24 pub span: Span,
25 pub options: ast::InlineAsmOptions,
27 pub span_with_comma: Span,
29}
30
31pub struct AsmAttrVec(pub ast::AttrVec);
34
35impl AsmAttrVec {
36 fn parse<'a>(p: &mut Parser<'a>) -> PResult<'a, Self> {
37 let attrs = p.parse_outer_attributes()?;
38
39 p.collect_tokens(None, attrs, ForceCollect::No, |_, attrs| {
40 Ok((Self(attrs), Trailing::No, UsePreAttrPos::No))
41 })
42 }
43}
44impl ast::HasAttrs for AsmAttrVec {
45 const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
47
48 fn attrs(&self) -> &[rustc_ast::Attribute] {
49 &self.0
50 }
51
52 fn visit_attrs(&mut self, f: impl FnOnce(&mut rustc_ast::AttrVec)) {
53 f(&mut self.0)
54 }
55}
56
57impl ast::HasTokens for AsmAttrVec {
58 fn tokens(&self) -> Option<&rustc_ast::tokenstream::LazyAttrTokenStream> {
59 None
60 }
61
62 fn tokens_mut(&mut self) -> Option<&mut Option<rustc_ast::tokenstream::LazyAttrTokenStream>> {
63 None
64 }
65}
66
67fn eat_operand_keyword<'a>(
76 p: &mut Parser<'a>,
77 exp: ExpKeywordPair,
78 asm_macro: AsmMacro,
79) -> PResult<'a, bool> {
80 if matches!(asm_macro, AsmMacro::Asm) {
81 Ok(p.eat_keyword(exp))
82 } else {
83 let span = p.token.span;
84 if p.eat_keyword_noexpect(exp.kw) {
85 let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() };
87 Err(p.dcx().create_err(errors::AsmUnsupportedOperand {
88 span,
89 symbol,
90 macro_name: asm_macro.macro_name(),
91 }))
92 } else {
93 Ok(false)
94 }
95 }
96}
97
98fn parse_asm_operand<'a>(
99 p: &mut Parser<'a>,
100 asm_macro: AsmMacro,
101) -> PResult<'a, Option<ast::InlineAsmOperand>> {
102 let dcx = p.dcx();
103
104 Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? {
105 let reg = parse_reg(p)?;
106 if p.eat_keyword(exp!(Underscore)) {
107 let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
108 return Err(err);
109 }
110 let expr = p.parse_expr()?;
111 ast::InlineAsmOperand::In { reg, expr }
112 } else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
113 let reg = parse_reg(p)?;
114 let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
115 ast::InlineAsmOperand::Out { reg, expr, late: false }
116 } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
117 let reg = parse_reg(p)?;
118 let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
119 ast::InlineAsmOperand::Out { reg, expr, late: true }
120 } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
121 let reg = parse_reg(p)?;
122 if p.eat_keyword(exp!(Underscore)) {
123 let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
124 return Err(err);
125 }
126 let expr = p.parse_expr()?;
127 if p.eat(exp!(FatArrow)) {
128 let out_expr =
129 if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
130 ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
131 } else {
132 ast::InlineAsmOperand::InOut { reg, expr, late: false }
133 }
134 } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
135 let reg = parse_reg(p)?;
136 if p.eat_keyword(exp!(Underscore)) {
137 let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
138 return Err(err);
139 }
140 let expr = p.parse_expr()?;
141 if p.eat(exp!(FatArrow)) {
142 let out_expr =
143 if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
144 ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
145 } else {
146 ast::InlineAsmOperand::InOut { reg, expr, late: true }
147 }
148 } else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
149 let block = p.parse_block()?;
150 ast::InlineAsmOperand::Label { block }
151 } else if p.eat_keyword(exp!(Const)) {
152 let anon_const = p.parse_expr_anon_const()?;
153 ast::InlineAsmOperand::Const { anon_const }
154 } else if p.eat_keyword(exp!(Sym)) {
155 let expr = p.parse_expr()?;
156 let ast::ExprKind::Path(qself, path) = &expr.kind else {
157 let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
158 return Err(err);
159 };
160 let sym =
161 ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() };
162 ast::InlineAsmOperand::Sym { sym }
163 } else {
164 return Ok(None);
165 }))
166}
167
168pub fn parse_asm_args<'a>(
170 p: &mut Parser<'a>,
171 sp: Span,
172 asm_macro: AsmMacro,
173) -> PResult<'a, Vec<AsmArg>> {
174 let dcx = p.dcx();
175
176 if p.token == token::Eof {
177 return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
178 }
179
180 let mut args = Vec::new();
181
182 let attributes = AsmAttrVec::parse(p)?;
183 let first_template = p.parse_expr()?;
184 args.push(AsmArg {
185 span: first_template.span,
186 kind: AsmArgKind::Template(first_template),
187 attributes,
188 });
189
190 let mut allow_templates = true;
191
192 while p.token != token::Eof {
193 if !p.eat(exp!(Comma)) {
194 if allow_templates {
195 return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
197 } else {
198 return Err(p.expect(exp!(Comma)).err().unwrap());
200 }
201 }
202
203 if p.token == token::Eof {
205 break;
206 }
207
208 let attributes = AsmAttrVec::parse(p)?;
209 let span_start = p.token.span;
210
211 if p.eat_keyword(exp!(ClobberAbi)) {
213 allow_templates = false;
214
215 args.push(AsmArg {
216 kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
217 span: span_start.to(p.prev_token.span),
218 attributes,
219 });
220
221 continue;
222 }
223
224 if p.eat_keyword(exp!(Options)) {
226 allow_templates = false;
227
228 args.push(AsmArg {
229 kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
230 span: span_start.to(p.prev_token.span),
231 attributes,
232 });
233
234 continue;
235 }
236
237 let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
239 let (ident, _) = p.token.ident().unwrap();
240 p.bump();
241 p.expect(exp!(Eq))?;
242 allow_templates = false;
243 Some(ident.name)
244 } else {
245 None
246 };
247
248 if let Some(op) = parse_asm_operand(p, asm_macro)? {
249 allow_templates = false;
250
251 args.push(AsmArg {
252 span: span_start.to(p.prev_token.span),
253 kind: AsmArgKind::Operand(name, op),
254 attributes,
255 });
256 } else if allow_templates {
257 let template = p.parse_expr()?;
258 match template.kind {
261 ast::ExprKind::Lit(token_lit)
262 if matches!(
263 token_lit.kind,
264 token::LitKind::Str | token::LitKind::StrRaw(_)
265 ) => {}
266 ast::ExprKind::MacCall(..) => {}
267 _ => {
268 let err = dcx.create_err(errors::AsmExpectedOther {
269 span: template.span,
270 is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
271 });
272 return Err(err);
273 }
274 }
275
276 args.push(AsmArg {
277 span: template.span,
278 kind: AsmArgKind::Template(template),
279 attributes,
280 });
281 } else {
282 p.unexpected_any()?
283 }
284 }
285
286 Ok(args)
287}
288
289fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec<AsmOption>> {
290 p.expect(exp!(OpenParen))?;
291
292 let mut asm_options = Vec::new();
293
294 while !p.eat(exp!(CloseParen)) {
295 const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
296 (exp!(Pure), ast::InlineAsmOptions::PURE),
297 (exp!(Nomem), ast::InlineAsmOptions::NOMEM),
298 (exp!(Readonly), ast::InlineAsmOptions::READONLY),
299 (exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS),
300 (exp!(Noreturn), ast::InlineAsmOptions::NORETURN),
301 (exp!(Nostack), ast::InlineAsmOptions::NOSTACK),
302 (exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND),
303 (exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX),
304 (exp!(Raw), ast::InlineAsmOptions::RAW),
305 ];
306
307 'blk: {
308 for (exp, options) in OPTIONS {
309 let kw_matched = if asm_macro.is_supported_option(options) {
311 p.eat_keyword(exp)
312 } else {
313 p.eat_keyword_noexpect(exp.kw)
314 };
315
316 if kw_matched {
317 let span = p.prev_token.span;
318 let span_with_comma =
319 if p.token == token::Comma { span.to(p.token.span) } else { span };
320
321 asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma });
322 break 'blk;
323 }
324 }
325
326 return p.unexpected_any();
327 }
328
329 if p.eat(exp!(CloseParen)) {
331 break;
332 }
333 p.expect(exp!(Comma))?;
334 }
335
336 Ok(asm_options)
337}
338
339fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
340 p.expect(exp!(OpenParen))?;
341
342 if p.eat(exp!(CloseParen)) {
343 return Err(p.dcx().create_err(errors::NonABI { span: p.token.span }));
344 }
345
346 let mut new_abis = Vec::new();
347 while !p.eat(exp!(CloseParen)) {
348 match p.parse_str_lit() {
349 Ok(str_lit) => {
350 new_abis.push((str_lit.symbol_unescaped, str_lit.span));
351 }
352 Err(opt_lit) => {
353 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
354 return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span }));
355 }
356 };
357
358 if p.eat(exp!(CloseParen)) {
360 break;
361 }
362 p.expect(exp!(Comma))?;
363 }
364
365 Ok(new_abis)
366}
367
368fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
369 p.expect(exp!(OpenParen))?;
370 let result = match p.token.uninterpolate().kind {
371 token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name),
372 token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
373 ast::InlineAsmRegOrRegClass::Reg(symbol)
374 }
375 _ => {
376 return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister {
377 span: p.token.span,
378 }));
379 }
380 };
381 p.bump();
382 p.expect(exp!(CloseParen))?;
383 Ok(result)
384}