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