1 use rustc_ast as ast;
2 use rustc_ast::ptr::P;
3 use rustc_ast::token::{self, Delimiter};
4 use rustc_ast::tokenstream::TokenStream;
5 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
6 use rustc_errors::PResult;
7 use rustc_expand::base::{self, *};
8 use rustc_index::bit_set::GrowableBitSet;
9 use rustc_parse::parser::Parser;
10 use rustc_parse_format as parse;
11 use rustc_session::lint;
12 use rustc_session::parse::ParseSess;
13 use rustc_span::symbol::Ident;
14 use rustc_span::symbol::{kw, sym, Symbol};
15 use rustc_span::{InnerSpan, Span};
16 use rustc_target::asm::InlineAsmArch;
17 use smallvec::smallvec;
18
19 use crate::errors;
20
21 pub struct AsmArgs {
22 pub templates: Vec<P<ast::Expr>>,
23 pub operands: Vec<(ast::InlineAsmOperand, Span)>,
24 named_args: FxIndexMap<Symbol, usize>,
25 reg_args: GrowableBitSet<usize>,
26 pub clobber_abis: Vec<(Symbol, Span)>,
27 options: ast::InlineAsmOptions,
28 pub options_spans: Vec<Span>,
29 }
30
parse_args<'a>( ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, is_global_asm: bool, ) -> PResult<'a, AsmArgs>31 fn parse_args<'a>(
32 ecx: &mut ExtCtxt<'a>,
33 sp: Span,
34 tts: TokenStream,
35 is_global_asm: bool,
36 ) -> PResult<'a, AsmArgs> {
37 let mut p = ecx.new_parser_from_tts(tts);
38 let sess = &ecx.sess.parse_sess;
39 parse_asm_args(&mut p, sess, sp, is_global_asm)
40 }
41
42 // Primarily public for rustfmt consumption.
43 // Internal consumers should continue to leverage `expand_asm`/`expand__global_asm`
parse_asm_args<'a>( p: &mut Parser<'a>, sess: &'a ParseSess, sp: Span, is_global_asm: bool, ) -> PResult<'a, AsmArgs>44 pub fn parse_asm_args<'a>(
45 p: &mut Parser<'a>,
46 sess: &'a ParseSess,
47 sp: Span,
48 is_global_asm: bool,
49 ) -> PResult<'a, AsmArgs> {
50 let diag = &sess.span_diagnostic;
51
52 if p.token == token::Eof {
53 return Err(diag.create_err(errors::AsmRequiresTemplate { span: sp }));
54 }
55
56 let first_template = p.parse_expr()?;
57 let mut args = AsmArgs {
58 templates: vec![first_template],
59 operands: vec![],
60 named_args: Default::default(),
61 reg_args: Default::default(),
62 clobber_abis: Vec::new(),
63 options: ast::InlineAsmOptions::empty(),
64 options_spans: vec![],
65 };
66
67 let mut allow_templates = true;
68 while p.token != token::Eof {
69 if !p.eat(&token::Comma) {
70 if allow_templates {
71 // After a template string, we always expect *only* a comma...
72 return Err(diag.create_err(errors::AsmExpectedComma { span: p.token.span }));
73 } else {
74 // ...after that delegate to `expect` to also include the other expected tokens.
75 return Err(p.expect(&token::Comma).err().unwrap());
76 }
77 }
78 if p.token == token::Eof {
79 break;
80 } // accept trailing commas
81
82 // Parse clobber_abi
83 if p.eat_keyword(sym::clobber_abi) {
84 parse_clobber_abi(p, &mut args)?;
85 allow_templates = false;
86 continue;
87 }
88
89 // Parse options
90 if p.eat_keyword(sym::options) {
91 parse_options(p, &mut args, is_global_asm)?;
92 allow_templates = false;
93 continue;
94 }
95
96 let span_start = p.token.span;
97
98 // Parse operand names
99 let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
100 let (ident, _) = p.token.ident().unwrap();
101 p.bump();
102 p.expect(&token::Eq)?;
103 allow_templates = false;
104 Some(ident.name)
105 } else {
106 None
107 };
108
109 let mut explicit_reg = false;
110 let op = if !is_global_asm && p.eat_keyword(kw::In) {
111 let reg = parse_reg(p, &mut explicit_reg)?;
112 if p.eat_keyword(kw::Underscore) {
113 let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
114 return Err(err);
115 }
116 let expr = p.parse_expr()?;
117 ast::InlineAsmOperand::In { reg, expr }
118 } else if !is_global_asm && p.eat_keyword(sym::out) {
119 let reg = parse_reg(p, &mut explicit_reg)?;
120 let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
121 ast::InlineAsmOperand::Out { reg, expr, late: false }
122 } else if !is_global_asm && p.eat_keyword(sym::lateout) {
123 let reg = parse_reg(p, &mut explicit_reg)?;
124 let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
125 ast::InlineAsmOperand::Out { reg, expr, late: true }
126 } else if !is_global_asm && p.eat_keyword(sym::inout) {
127 let reg = parse_reg(p, &mut explicit_reg)?;
128 if p.eat_keyword(kw::Underscore) {
129 let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
130 return Err(err);
131 }
132 let expr = p.parse_expr()?;
133 if p.eat(&token::FatArrow) {
134 let out_expr =
135 if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
136 ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
137 } else {
138 ast::InlineAsmOperand::InOut { reg, expr, late: false }
139 }
140 } else if !is_global_asm && p.eat_keyword(sym::inlateout) {
141 let reg = parse_reg(p, &mut explicit_reg)?;
142 if p.eat_keyword(kw::Underscore) {
143 let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
144 return Err(err);
145 }
146 let expr = p.parse_expr()?;
147 if p.eat(&token::FatArrow) {
148 let out_expr =
149 if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
150 ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
151 } else {
152 ast::InlineAsmOperand::InOut { reg, expr, late: true }
153 }
154 } else if p.eat_keyword(kw::Const) {
155 let anon_const = p.parse_expr_anon_const()?;
156 ast::InlineAsmOperand::Const { anon_const }
157 } else if p.eat_keyword(sym::sym) {
158 let expr = p.parse_expr()?;
159 let ast::ExprKind::Path(qself, path) = &expr.kind else {
160 let err = diag
161 .create_err(errors::AsmSymNoPath { span: expr.span });
162 return Err(err);
163 };
164 let sym = ast::InlineAsmSym {
165 id: ast::DUMMY_NODE_ID,
166 qself: qself.clone(),
167 path: path.clone(),
168 };
169 ast::InlineAsmOperand::Sym { sym }
170 } else if allow_templates {
171 let template = p.parse_expr()?;
172 // If it can't possibly expand to a string, provide diagnostics here to include other
173 // things it could have been.
174 match template.kind {
175 ast::ExprKind::Lit(token_lit)
176 if matches!(
177 token_lit.kind,
178 token::LitKind::Str | token::LitKind::StrRaw(_)
179 ) => {}
180 ast::ExprKind::MacCall(..) => {}
181 _ => {
182 let err = diag.create_err(errors::AsmExpectedOther {
183 span: template.span,
184 is_global_asm,
185 });
186 return Err(err);
187 }
188 }
189 args.templates.push(template);
190 continue;
191 } else {
192 return p.unexpected();
193 };
194
195 allow_templates = false;
196 let span = span_start.to(p.prev_token.span);
197 let slot = args.operands.len();
198 args.operands.push((op, span));
199
200 // Validate the order of named, positional & explicit register operands and
201 // clobber_abi/options. We do this at the end once we have the full span
202 // of the argument available.
203 if explicit_reg {
204 if name.is_some() {
205 diag.emit_err(errors::AsmExplicitRegisterName { span });
206 }
207 args.reg_args.insert(slot);
208 } else if let Some(name) = name {
209 if let Some(&prev) = args.named_args.get(&name) {
210 diag.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 });
211 continue;
212 }
213 args.named_args.insert(name, slot);
214 } else {
215 if !args.named_args.is_empty() || !args.reg_args.is_empty() {
216 let named = args.named_args.values().map(|p| args.operands[*p].1).collect();
217 let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect();
218
219 diag.emit_err(errors::AsmPositionalAfter { span, named, explicit });
220 }
221 }
222 }
223
224 if args.options.contains(ast::InlineAsmOptions::NOMEM)
225 && args.options.contains(ast::InlineAsmOptions::READONLY)
226 {
227 let spans = args.options_spans.clone();
228 diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
229 }
230 if args.options.contains(ast::InlineAsmOptions::PURE)
231 && args.options.contains(ast::InlineAsmOptions::NORETURN)
232 {
233 let spans = args.options_spans.clone();
234 diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
235 }
236 if args.options.contains(ast::InlineAsmOptions::PURE)
237 && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
238 {
239 let spans = args.options_spans.clone();
240 diag.emit_err(errors::AsmPureCombine { spans });
241 }
242
243 let mut have_real_output = false;
244 let mut outputs_sp = vec![];
245 let mut regclass_outputs = vec![];
246 for (op, op_sp) in &args.operands {
247 match op {
248 ast::InlineAsmOperand::Out { reg, expr, .. }
249 | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
250 outputs_sp.push(*op_sp);
251 have_real_output |= expr.is_some();
252 if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
253 regclass_outputs.push(*op_sp);
254 }
255 }
256 ast::InlineAsmOperand::InOut { reg, .. } => {
257 outputs_sp.push(*op_sp);
258 have_real_output = true;
259 if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg {
260 regclass_outputs.push(*op_sp);
261 }
262 }
263 _ => {}
264 }
265 }
266 if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
267 diag.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
268 }
269 if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
270 let err = diag.create_err(errors::AsmNoReturn { outputs_sp });
271 // Bail out now since this is likely to confuse MIR
272 return Err(err);
273 }
274
275 if args.clobber_abis.len() > 0 {
276 if is_global_asm {
277 let err = diag.create_err(errors::GlobalAsmClobberAbi {
278 spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
279 });
280
281 // Bail out now since this is likely to confuse later stages
282 return Err(err);
283 }
284 if !regclass_outputs.is_empty() {
285 diag.emit_err(errors::AsmClobberNoReg {
286 spans: regclass_outputs,
287 clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
288 });
289 }
290 }
291
292 Ok(args)
293 }
294
295 /// Report a duplicate option error.
296 ///
297 /// This function must be called immediately after the option token is parsed.
298 /// Otherwise, the suggestion will be incorrect.
err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span)299 fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) {
300 // Tool-only output
301 let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span };
302 p.sess.span_diagnostic.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
303 }
304
305 /// Try to set the provided option in the provided `AsmArgs`.
306 /// If it is already set, report a duplicate option error.
307 ///
308 /// This function must be called immediately after the option token is parsed.
309 /// Otherwise, the error will not point to the correct spot.
try_set_option<'a>( p: &mut Parser<'a>, args: &mut AsmArgs, symbol: Symbol, option: ast::InlineAsmOptions, )310 fn try_set_option<'a>(
311 p: &mut Parser<'a>,
312 args: &mut AsmArgs,
313 symbol: Symbol,
314 option: ast::InlineAsmOptions,
315 ) {
316 if !args.options.contains(option) {
317 args.options |= option;
318 } else {
319 err_duplicate_option(p, symbol, p.prev_token.span);
320 }
321 }
322
parse_options<'a>( p: &mut Parser<'a>, args: &mut AsmArgs, is_global_asm: bool, ) -> PResult<'a, ()>323 fn parse_options<'a>(
324 p: &mut Parser<'a>,
325 args: &mut AsmArgs,
326 is_global_asm: bool,
327 ) -> PResult<'a, ()> {
328 let span_start = p.prev_token.span;
329
330 p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
331
332 while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
333 if !is_global_asm && p.eat_keyword(sym::pure) {
334 try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE);
335 } else if !is_global_asm && p.eat_keyword(sym::nomem) {
336 try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM);
337 } else if !is_global_asm && p.eat_keyword(sym::readonly) {
338 try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY);
339 } else if !is_global_asm && p.eat_keyword(sym::preserves_flags) {
340 try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS);
341 } else if !is_global_asm && p.eat_keyword(sym::noreturn) {
342 try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN);
343 } else if !is_global_asm && p.eat_keyword(sym::nostack) {
344 try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK);
345 } else if !is_global_asm && p.eat_keyword(sym::may_unwind) {
346 try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::MAY_UNWIND);
347 } else if p.eat_keyword(sym::att_syntax) {
348 try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
349 } else if p.eat_keyword(kw::Raw) {
350 try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::RAW);
351 } else {
352 return p.unexpected();
353 }
354
355 // Allow trailing commas
356 if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
357 break;
358 }
359 p.expect(&token::Comma)?;
360 }
361
362 let new_span = span_start.to(p.prev_token.span);
363 args.options_spans.push(new_span);
364
365 Ok(())
366 }
367
parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()>368 fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> {
369 let span_start = p.prev_token.span;
370
371 p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
372
373 if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
374 return Err(p.sess.span_diagnostic.create_err(errors::NonABI { span: p.token.span }));
375 }
376
377 let mut new_abis = Vec::new();
378 while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
379 match p.parse_str_lit() {
380 Ok(str_lit) => {
381 new_abis.push((str_lit.symbol_unescaped, str_lit.span));
382 }
383 Err(opt_lit) => {
384 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
385 let mut err =
386 p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
387 err.span_label(span, "not a string literal");
388 return Err(err);
389 }
390 };
391
392 // Allow trailing commas
393 if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
394 break;
395 }
396 p.expect(&token::Comma)?;
397 }
398
399 let full_span = span_start.to(p.prev_token.span);
400
401 match &new_abis[..] {
402 // should have errored above during parsing
403 [] => unreachable!(),
404 [(abi, _span)] => args.clobber_abis.push((*abi, full_span)),
405 [abis @ ..] => {
406 for (abi, span) in abis {
407 args.clobber_abis.push((*abi, *span));
408 }
409 }
410 }
411
412 Ok(())
413 }
414
parse_reg<'a>( p: &mut Parser<'a>, explicit_reg: &mut bool, ) -> PResult<'a, ast::InlineAsmRegOrRegClass>415 fn parse_reg<'a>(
416 p: &mut Parser<'a>,
417 explicit_reg: &mut bool,
418 ) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
419 p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
420 let result = match p.token.uninterpolate().kind {
421 token::Ident(name, false) => ast::InlineAsmRegOrRegClass::RegClass(name),
422 token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
423 *explicit_reg = true;
424 ast::InlineAsmRegOrRegClass::Reg(symbol)
425 }
426 _ => {
427 return Err(p.sess.create_err(errors::ExpectedRegisterClassOrExplicitRegister {
428 span: p.token.span,
429 }));
430 }
431 };
432 p.bump();
433 p.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
434 Ok(result)
435 }
436
expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm>437 fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm> {
438 let mut template = vec![];
439 // Register operands are implicitly used since they are not allowed to be
440 // referenced in the template string.
441 let mut used = vec![false; args.operands.len()];
442 for pos in args.reg_args.iter() {
443 used[pos] = true;
444 }
445 let named_pos: FxHashMap<usize, Symbol> =
446 args.named_args.iter().map(|(&sym, &idx)| (idx, sym)).collect();
447 let mut line_spans = Vec::with_capacity(args.templates.len());
448 let mut curarg = 0;
449
450 let mut template_strs = Vec::with_capacity(args.templates.len());
451
452 for (i, template_expr) in args.templates.into_iter().enumerate() {
453 if i != 0 {
454 template.push(ast::InlineAsmTemplatePiece::String("\n".to_string()));
455 }
456
457 let msg = "asm template must be a string literal";
458 let template_sp = template_expr.span;
459 let (template_str, template_style, template_span) =
460 match expr_to_spanned_string(ecx, template_expr, msg) {
461 Ok(template_part) => template_part,
462 Err(err) => {
463 if let Some((mut err, _)) = err {
464 err.emit();
465 }
466 return None;
467 }
468 };
469
470 let str_style = match template_style {
471 ast::StrStyle::Cooked => None,
472 ast::StrStyle::Raw(raw) => Some(raw as usize),
473 };
474
475 let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok();
476 template_strs.push((
477 template_str,
478 template_snippet.as_deref().map(Symbol::intern),
479 template_sp,
480 ));
481 let template_str = template_str.as_str();
482
483 if let Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) = ecx.sess.asm_arch {
484 let find_span = |needle: &str| -> Span {
485 if let Some(snippet) = &template_snippet {
486 if let Some(pos) = snippet.find(needle) {
487 let end = pos
488 + snippet[pos..]
489 .find(|c| matches!(c, '\n' | ';' | '\\' | '"'))
490 .unwrap_or(snippet[pos..].len() - 1);
491 let inner = InnerSpan::new(pos, end);
492 return template_sp.from_inner(inner);
493 }
494 }
495 template_sp
496 };
497
498 if template_str.contains(".intel_syntax") {
499 ecx.parse_sess().buffer_lint(
500 lint::builtin::BAD_ASM_STYLE,
501 find_span(".intel_syntax"),
502 ecx.current_expansion.lint_node_id,
503 "avoid using `.intel_syntax`, Intel syntax is the default",
504 );
505 }
506 if template_str.contains(".att_syntax") {
507 ecx.parse_sess().buffer_lint(
508 lint::builtin::BAD_ASM_STYLE,
509 find_span(".att_syntax"),
510 ecx.current_expansion.lint_node_id,
511 "avoid using `.att_syntax`, prefer using `options(att_syntax)` instead",
512 );
513 }
514 }
515
516 // Don't treat raw asm as a format string.
517 if args.options.contains(ast::InlineAsmOptions::RAW) {
518 template.push(ast::InlineAsmTemplatePiece::String(template_str.to_string()));
519 let template_num_lines = 1 + template_str.matches('\n').count();
520 line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
521 continue;
522 }
523
524 let mut parser = parse::Parser::new(
525 template_str,
526 str_style,
527 template_snippet,
528 false,
529 parse::ParseMode::InlineAsm,
530 );
531 parser.curarg = curarg;
532
533 let mut unverified_pieces = Vec::new();
534 while let Some(piece) = parser.next() {
535 if !parser.errors.is_empty() {
536 break;
537 } else {
538 unverified_pieces.push(piece);
539 }
540 }
541
542 if !parser.errors.is_empty() {
543 let err = parser.errors.remove(0);
544 let err_sp = template_span.from_inner(InnerSpan::new(err.span.start, err.span.end));
545 let msg = format!("invalid asm template string: {}", err.description);
546 let mut e = ecx.struct_span_err(err_sp, msg);
547 e.span_label(err_sp, err.label + " in asm template string");
548 if let Some(note) = err.note {
549 e.note(note);
550 }
551 if let Some((label, span)) = err.secondary_label {
552 let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end));
553 e.span_label(err_sp, label);
554 }
555 e.emit();
556 return None;
557 }
558
559 curarg = parser.curarg;
560
561 let mut arg_spans = parser
562 .arg_places
563 .iter()
564 .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end)));
565 for piece in unverified_pieces {
566 match piece {
567 parse::Piece::String(s) => {
568 template.push(ast::InlineAsmTemplatePiece::String(s.to_string()))
569 }
570 parse::Piece::NextArgument(arg) => {
571 let span = arg_spans.next().unwrap_or(template_sp);
572
573 let operand_idx = match arg.position {
574 parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
575 if idx >= args.operands.len()
576 || named_pos.contains_key(&idx)
577 || args.reg_args.contains(idx)
578 {
579 let msg = format!("invalid reference to argument at index {}", idx);
580 let mut err = ecx.struct_span_err(span, msg);
581 err.span_label(span, "from here");
582
583 let positional_args = args.operands.len()
584 - args.named_args.len()
585 - args.reg_args.len();
586 let positional = if positional_args != args.operands.len() {
587 "positional "
588 } else {
589 ""
590 };
591 let msg = match positional_args {
592 0 => format!("no {}arguments were given", positional),
593 1 => format!("there is 1 {}argument", positional),
594 x => format!("there are {} {}arguments", x, positional),
595 };
596 err.note(msg);
597
598 if named_pos.contains_key(&idx) {
599 err.span_label(args.operands[idx].1, "named argument");
600 err.span_note(
601 args.operands[idx].1,
602 "named arguments cannot be referenced by position",
603 );
604 } else if args.reg_args.contains(idx) {
605 err.span_label(
606 args.operands[idx].1,
607 "explicit register argument",
608 );
609 err.span_note(
610 args.operands[idx].1,
611 "explicit register arguments cannot be used in the asm template",
612 );
613 err.span_help(
614 args.operands[idx].1,
615 "use the register name directly in the assembly code",
616 );
617 }
618 err.emit();
619 None
620 } else {
621 Some(idx)
622 }
623 }
624 parse::ArgumentNamed(name) => {
625 match args.named_args.get(&Symbol::intern(name)) {
626 Some(&idx) => Some(idx),
627 None => {
628 let msg = format!("there is no argument named `{}`", name);
629 let span = arg.position_span;
630 ecx.struct_span_err(
631 template_span
632 .from_inner(InnerSpan::new(span.start, span.end)),
633 msg,
634 )
635 .emit();
636 None
637 }
638 }
639 }
640 };
641
642 let mut chars = arg.format.ty.chars();
643 let mut modifier = chars.next();
644 if chars.next().is_some() {
645 let span = arg
646 .format
647 .ty_span
648 .map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end)))
649 .unwrap_or(template_sp);
650 ecx.emit_err(errors::AsmModifierInvalid { span });
651 modifier = None;
652 }
653
654 if let Some(operand_idx) = operand_idx {
655 used[operand_idx] = true;
656 template.push(ast::InlineAsmTemplatePiece::Placeholder {
657 operand_idx,
658 modifier,
659 span,
660 });
661 }
662 }
663 }
664 }
665
666 if parser.line_spans.is_empty() {
667 let template_num_lines = 1 + template_str.matches('\n').count();
668 line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
669 } else {
670 line_spans.extend(
671 parser
672 .line_spans
673 .iter()
674 .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))),
675 );
676 };
677 }
678
679 let mut unused_operands = vec![];
680 let mut help_str = String::new();
681 for (idx, used) in used.into_iter().enumerate() {
682 if !used {
683 let msg = if let Some(sym) = named_pos.get(&idx) {
684 help_str.push_str(&format!(" {{{}}}", sym));
685 "named argument never used"
686 } else {
687 help_str.push_str(&format!(" {{{}}}", idx));
688 "argument never used"
689 };
690 unused_operands.push((args.operands[idx].1, msg));
691 }
692 }
693 match unused_operands.len() {
694 0 => {}
695 1 => {
696 let (sp, msg) = unused_operands.into_iter().next().unwrap();
697 let mut err = ecx.struct_span_err(sp, msg);
698 err.span_label(sp, msg);
699 err.help(format!(
700 "if this argument is intentionally unused, \
701 consider using it in an asm comment: `\"/*{} */\"`",
702 help_str
703 ));
704 err.emit();
705 }
706 _ => {
707 let mut err = ecx.struct_span_err(
708 unused_operands.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
709 "multiple unused asm arguments",
710 );
711 for (sp, msg) in unused_operands {
712 err.span_label(sp, msg);
713 }
714 err.help(format!(
715 "if these arguments are intentionally unused, \
716 consider using them in an asm comment: `\"/*{} */\"`",
717 help_str
718 ));
719 err.emit();
720 }
721 }
722
723 Some(ast::InlineAsm {
724 template,
725 template_strs: template_strs.into_boxed_slice(),
726 operands: args.operands,
727 clobber_abis: args.clobber_abis,
728 options: args.options,
729 line_spans,
730 })
731 }
732
expand_asm<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> Box<dyn base::MacResult + 'cx>733 pub(super) fn expand_asm<'cx>(
734 ecx: &'cx mut ExtCtxt<'_>,
735 sp: Span,
736 tts: TokenStream,
737 ) -> Box<dyn base::MacResult + 'cx> {
738 match parse_args(ecx, sp, tts, false) {
739 Ok(args) => {
740 let expr = if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
741 P(ast::Expr {
742 id: ast::DUMMY_NODE_ID,
743 kind: ast::ExprKind::InlineAsm(P(inline_asm)),
744 span: sp,
745 attrs: ast::AttrVec::new(),
746 tokens: None,
747 })
748 } else {
749 DummyResult::raw_expr(sp, true)
750 };
751 MacEager::expr(expr)
752 }
753 Err(mut err) => {
754 err.emit();
755 DummyResult::any(sp)
756 }
757 }
758 }
759
expand_global_asm<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> Box<dyn base::MacResult + 'cx>760 pub(super) fn expand_global_asm<'cx>(
761 ecx: &'cx mut ExtCtxt<'_>,
762 sp: Span,
763 tts: TokenStream,
764 ) -> Box<dyn base::MacResult + 'cx> {
765 match parse_args(ecx, sp, tts, true) {
766 Ok(args) => {
767 if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
768 MacEager::items(smallvec![P(ast::Item {
769 ident: Ident::empty(),
770 attrs: ast::AttrVec::new(),
771 id: ast::DUMMY_NODE_ID,
772 kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
773 vis: ast::Visibility {
774 span: sp.shrink_to_lo(),
775 kind: ast::VisibilityKind::Inherited,
776 tokens: None,
777 },
778 span: ecx.with_def_site_ctxt(sp),
779 tokens: None,
780 })])
781 } else {
782 DummyResult::any(sp)
783 }
784 }
785 Err(mut err) => {
786 err.emit();
787 DummyResult::any(sp)
788 }
789 }
790 }
791