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_abis: Vec<(Symbol, Span)>,
23     options: ast::InlineAsmOptions,
24     options_spans: Vec<Span>,
25 }
26 
parse_args<'a>( ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, is_global_asm: bool, ) -> Result<AsmArgs, DiagnosticBuilder<'a>>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_abis: Vec::new(),
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_abis.last() {
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 
326     if args.clobber_abis.len() > 0 {
327         if is_global_asm {
328             let err = ecx.struct_span_err(
329                 args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(),
330                 "`clobber_abi` cannot be used with `global_asm!`",
331             );
332 
333             // Bail out now since this is likely to confuse later stages
334             return Err(err);
335         }
336         if !regclass_outputs.is_empty() {
337             ecx.struct_span_err(
338                 regclass_outputs.clone(),
339                 "asm with `clobber_abi` must specify explicit registers for outputs",
340             )
341             .span_labels(
342                 args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(),
343                 "clobber_abi",
344             )
345             .span_labels(regclass_outputs, "generic outputs")
346             .emit();
347         }
348     }
349 
350     Ok(args)
351 }
352 
353 /// Report a duplicate option error.
354 ///
355 /// This function must be called immediately after the option token is parsed.
356 /// Otherwise, the suggestion will be incorrect.
err_duplicate_option<'a>(p: &mut Parser<'a>, symbol: Symbol, span: Span)357 fn err_duplicate_option<'a>(p: &mut Parser<'a>, symbol: Symbol, span: Span) {
358     let mut err = p
359         .sess
360         .span_diagnostic
361         .struct_span_err(span, &format!("the `{}` option was already provided", symbol));
362     err.span_label(span, "this option was already provided");
363 
364     // Tool-only output
365     let mut full_span = span;
366     if p.token.kind == token::Comma {
367         full_span = full_span.to(p.token.span);
368     }
369     err.tool_only_span_suggestion(
370         full_span,
371         "remove this option",
372         String::new(),
373         Applicability::MachineApplicable,
374     );
375 
376     err.emit();
377 }
378 
379 /// Try to set the provided option in the provided `AsmArgs`.
380 /// If it is already set, report a duplicate option error.
381 ///
382 /// This function must be called immediately after the option token is parsed.
383 /// 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, )384 fn try_set_option<'a>(
385     p: &mut Parser<'a>,
386     args: &mut AsmArgs,
387     symbol: Symbol,
388     option: ast::InlineAsmOptions,
389 ) {
390     if !args.options.contains(option) {
391         args.options |= option;
392     } else {
393         err_duplicate_option(p, symbol, p.prev_token.span);
394     }
395 }
396 
parse_options<'a>( p: &mut Parser<'a>, args: &mut AsmArgs, is_global_asm: bool, ) -> Result<(), DiagnosticBuilder<'a>>397 fn parse_options<'a>(
398     p: &mut Parser<'a>,
399     args: &mut AsmArgs,
400     is_global_asm: bool,
401 ) -> Result<(), DiagnosticBuilder<'a>> {
402     let span_start = p.prev_token.span;
403 
404     p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
405 
406     while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
407         if !is_global_asm && p.eat_keyword(sym::pure) {
408             try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE);
409         } else if !is_global_asm && p.eat_keyword(sym::nomem) {
410             try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM);
411         } else if !is_global_asm && p.eat_keyword(sym::readonly) {
412             try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY);
413         } else if !is_global_asm && p.eat_keyword(sym::preserves_flags) {
414             try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS);
415         } else if !is_global_asm && p.eat_keyword(sym::noreturn) {
416             try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN);
417         } else if !is_global_asm && p.eat_keyword(sym::nostack) {
418             try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK);
419         } else if p.eat_keyword(sym::att_syntax) {
420             try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
421         } else if p.eat_keyword(kw::Raw) {
422             try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::RAW);
423         } else {
424             return p.unexpected();
425         }
426 
427         // Allow trailing commas
428         if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
429             break;
430         }
431         p.expect(&token::Comma)?;
432     }
433 
434     let new_span = span_start.to(p.prev_token.span);
435     args.options_spans.push(new_span);
436 
437     Ok(())
438 }
439 
parse_clobber_abi<'a>( p: &mut Parser<'a>, args: &mut AsmArgs, ) -> Result<(), DiagnosticBuilder<'a>>440 fn parse_clobber_abi<'a>(
441     p: &mut Parser<'a>,
442     args: &mut AsmArgs,
443 ) -> Result<(), DiagnosticBuilder<'a>> {
444     let span_start = p.prev_token.span;
445 
446     p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
447 
448     if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
449         let err = p.sess.span_diagnostic.struct_span_err(
450             p.token.span,
451             "at least one abi must be provided as an argument to `clobber_abi`",
452         );
453         return Err(err);
454     }
455 
456     let mut new_abis = Vec::new();
457     loop {
458         match p.parse_str_lit() {
459             Ok(str_lit) => {
460                 new_abis.push((str_lit.symbol_unescaped, str_lit.span));
461             }
462             Err(opt_lit) => {
463                 // If the non-string literal is a closing paren then it's the end of the list and is fine
464                 if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
465                     break;
466                 }
467                 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
468                 let mut err =
469                     p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
470                 err.span_label(span, "not a string literal");
471                 return Err(err);
472             }
473         };
474 
475         // Allow trailing commas
476         if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
477             break;
478         }
479         p.expect(&token::Comma)?;
480     }
481 
482     let full_span = span_start.to(p.prev_token.span);
483 
484     if !args.options_spans.is_empty() {
485         let mut err = p
486             .sess
487             .span_diagnostic
488             .struct_span_err(full_span, "clobber_abi is not allowed after options");
489         err.span_labels(args.options_spans.clone(), "options");
490         return Err(err);
491     }
492 
493     match &new_abis[..] {
494         // should have errored above during parsing
495         [] => unreachable!(),
496         [(abi, _span)] => args.clobber_abis.push((*abi, full_span)),
497         [abis @ ..] => {
498             for (abi, span) in abis {
499                 args.clobber_abis.push((*abi, *span));
500             }
501         }
502     }
503 
504     Ok(())
505 }
506 
parse_reg<'a>( p: &mut Parser<'a>, explicit_reg: &mut bool, ) -> Result<ast::InlineAsmRegOrRegClass, DiagnosticBuilder<'a>>507 fn parse_reg<'a>(
508     p: &mut Parser<'a>,
509     explicit_reg: &mut bool,
510 ) -> Result<ast::InlineAsmRegOrRegClass, DiagnosticBuilder<'a>> {
511     p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
512     let result = match p.token.uninterpolate().kind {
513         token::Ident(name, false) => ast::InlineAsmRegOrRegClass::RegClass(name),
514         token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
515             *explicit_reg = true;
516             ast::InlineAsmRegOrRegClass::Reg(symbol)
517         }
518         _ => {
519             return Err(
520                 p.struct_span_err(p.token.span, "expected register class or explicit register")
521             );
522         }
523     };
524     p.bump();
525     p.expect(&token::CloseDelim(token::DelimToken::Paren))?;
526     Ok(result)
527 }
528 
expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm>529 fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm> {
530     let mut template = vec![];
531     // Register operands are implicitly used since they are not allowed to be
532     // referenced in the template string.
533     let mut used = vec![false; args.operands.len()];
534     for pos in &args.reg_args {
535         used[*pos] = true;
536     }
537     let named_pos: FxHashMap<usize, Symbol> =
538         args.named_args.iter().map(|(&sym, &idx)| (idx, sym)).collect();
539     let mut line_spans = Vec::with_capacity(args.templates.len());
540     let mut curarg = 0;
541 
542     let mut template_strs = Vec::with_capacity(args.templates.len());
543 
544     for template_expr in args.templates.into_iter() {
545         if !template.is_empty() {
546             template.push(ast::InlineAsmTemplatePiece::String("\n".to_string()));
547         }
548 
549         let msg = "asm template must be a string literal";
550         let template_sp = template_expr.span;
551         let (template_str, template_style, template_span) =
552             match expr_to_spanned_string(ecx, template_expr, msg) {
553                 Ok(template_part) => template_part,
554                 Err(err) => {
555                     if let Some((mut err, _)) = err {
556                         err.emit();
557                     }
558                     return None;
559                 }
560             };
561 
562         let str_style = match template_style {
563             ast::StrStyle::Cooked => None,
564             ast::StrStyle::Raw(raw) => Some(raw as usize),
565         };
566 
567         let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok();
568         template_strs.push((
569             template_str,
570             template_snippet.as_ref().map(|s| Symbol::intern(s)),
571             template_sp,
572         ));
573         let template_str = &template_str.as_str();
574 
575         if let Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) = ecx.sess.asm_arch {
576             let find_span = |needle: &str| -> Span {
577                 if let Some(snippet) = &template_snippet {
578                     if let Some(pos) = snippet.find(needle) {
579                         let end = pos
580                             + snippet[pos..]
581                                 .find(|c| matches!(c, '\n' | ';' | '\\' | '"'))
582                                 .unwrap_or(snippet[pos..].len() - 1);
583                         let inner = InnerSpan::new(pos, end);
584                         return template_sp.from_inner(inner);
585                     }
586                 }
587                 template_sp
588             };
589 
590             if template_str.contains(".intel_syntax") {
591                 ecx.parse_sess().buffer_lint(
592                     lint::builtin::BAD_ASM_STYLE,
593                     find_span(".intel_syntax"),
594                     ecx.current_expansion.lint_node_id,
595                     "avoid using `.intel_syntax`, Intel syntax is the default",
596                 );
597             }
598             if template_str.contains(".att_syntax") {
599                 ecx.parse_sess().buffer_lint(
600                     lint::builtin::BAD_ASM_STYLE,
601                     find_span(".att_syntax"),
602                     ecx.current_expansion.lint_node_id,
603                     "avoid using `.att_syntax`, prefer using `options(att_syntax)` instead",
604                 );
605             }
606         }
607 
608         // Don't treat raw asm as a format string.
609         if args.options.contains(ast::InlineAsmOptions::RAW) {
610             template.push(ast::InlineAsmTemplatePiece::String(template_str.to_string()));
611             let template_num_lines = 1 + template_str.matches('\n').count();
612             line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
613             continue;
614         }
615 
616         let mut parser = parse::Parser::new(
617             template_str,
618             str_style,
619             template_snippet,
620             false,
621             parse::ParseMode::InlineAsm,
622         );
623         parser.curarg = curarg;
624 
625         let mut unverified_pieces = Vec::new();
626         while let Some(piece) = parser.next() {
627             if !parser.errors.is_empty() {
628                 break;
629             } else {
630                 unverified_pieces.push(piece);
631             }
632         }
633 
634         if !parser.errors.is_empty() {
635             let err = parser.errors.remove(0);
636             let err_sp = template_span.from_inner(err.span);
637             let msg = &format!("invalid asm template string: {}", err.description);
638             let mut e = ecx.struct_span_err(err_sp, msg);
639             e.span_label(err_sp, err.label + " in asm template string");
640             if let Some(note) = err.note {
641                 e.note(&note);
642             }
643             if let Some((label, span)) = err.secondary_label {
644                 let err_sp = template_span.from_inner(span);
645                 e.span_label(err_sp, label);
646             }
647             e.emit();
648             return None;
649         }
650 
651         curarg = parser.curarg;
652 
653         let mut arg_spans = parser.arg_places.iter().map(|span| template_span.from_inner(*span));
654         for piece in unverified_pieces {
655             match piece {
656                 parse::Piece::String(s) => {
657                     template.push(ast::InlineAsmTemplatePiece::String(s.to_string()))
658                 }
659                 parse::Piece::NextArgument(arg) => {
660                     let span = arg_spans.next().unwrap_or(template_sp);
661 
662                     let operand_idx = match arg.position {
663                         parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
664                             if idx >= args.operands.len()
665                                 || named_pos.contains_key(&idx)
666                                 || args.reg_args.contains(&idx)
667                             {
668                                 let msg = format!("invalid reference to argument at index {}", idx);
669                                 let mut err = ecx.struct_span_err(span, &msg);
670                                 err.span_label(span, "from here");
671 
672                                 let positional_args = args.operands.len()
673                                     - args.named_args.len()
674                                     - args.reg_args.len();
675                                 let positional = if positional_args != args.operands.len() {
676                                     "positional "
677                                 } else {
678                                     ""
679                                 };
680                                 let msg = match positional_args {
681                                     0 => format!("no {}arguments were given", positional),
682                                     1 => format!("there is 1 {}argument", positional),
683                                     x => format!("there are {} {}arguments", x, positional),
684                                 };
685                                 err.note(&msg);
686 
687                                 if named_pos.contains_key(&idx) {
688                                     err.span_label(args.operands[idx].1, "named argument");
689                                     err.span_note(
690                                         args.operands[idx].1,
691                                         "named arguments cannot be referenced by position",
692                                     );
693                                 } else if args.reg_args.contains(&idx) {
694                                     err.span_label(
695                                         args.operands[idx].1,
696                                         "explicit register argument",
697                                     );
698                                     err.span_note(
699                                         args.operands[idx].1,
700                                         "explicit register arguments cannot be used in the asm template",
701                                     );
702                                 }
703                                 err.emit();
704                                 None
705                             } else {
706                                 Some(idx)
707                             }
708                         }
709                         parse::ArgumentNamed(name) => match args.named_args.get(&name) {
710                             Some(&idx) => Some(idx),
711                             None => {
712                                 let msg = format!("there is no argument named `{}`", name);
713                                 ecx.struct_span_err(span, &msg[..]).emit();
714                                 None
715                             }
716                         },
717                     };
718 
719                     let mut chars = arg.format.ty.chars();
720                     let mut modifier = chars.next();
721                     if chars.next().is_some() {
722                         let span = arg
723                             .format
724                             .ty_span
725                             .map(|sp| template_sp.from_inner(sp))
726                             .unwrap_or(template_sp);
727                         ecx.struct_span_err(
728                             span,
729                             "asm template modifier must be a single character",
730                         )
731                         .emit();
732                         modifier = None;
733                     }
734 
735                     if let Some(operand_idx) = operand_idx {
736                         used[operand_idx] = true;
737                         template.push(ast::InlineAsmTemplatePiece::Placeholder {
738                             operand_idx,
739                             modifier,
740                             span,
741                         });
742                     }
743                 }
744             }
745         }
746 
747         if parser.line_spans.is_empty() {
748             let template_num_lines = 1 + template_str.matches('\n').count();
749             line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
750         } else {
751             line_spans.extend(parser.line_spans.iter().map(|span| template_span.from_inner(*span)));
752         };
753     }
754 
755     let mut unused_operands = vec![];
756     let mut help_str = String::new();
757     for (idx, used) in used.into_iter().enumerate() {
758         if !used {
759             let msg = if let Some(sym) = named_pos.get(&idx) {
760                 help_str.push_str(&format!(" {{{}}}", sym));
761                 "named argument never used"
762             } else {
763                 help_str.push_str(&format!(" {{{}}}", idx));
764                 "argument never used"
765             };
766             unused_operands.push((args.operands[idx].1, msg));
767         }
768     }
769     match unused_operands.len() {
770         0 => {}
771         1 => {
772             let (sp, msg) = unused_operands.into_iter().next().unwrap();
773             let mut err = ecx.struct_span_err(sp, msg);
774             err.span_label(sp, msg);
775             err.help(&format!(
776                 "if this argument is intentionally unused, \
777                  consider using it in an asm comment: `\"/*{} */\"`",
778                 help_str
779             ));
780             err.emit();
781         }
782         _ => {
783             let mut err = ecx.struct_span_err(
784                 unused_operands.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
785                 "multiple unused asm arguments",
786             );
787             for (sp, msg) in unused_operands {
788                 err.span_label(sp, msg);
789             }
790             err.help(&format!(
791                 "if these arguments are intentionally unused, \
792                  consider using them in an asm comment: `\"/*{} */\"`",
793                 help_str
794             ));
795             err.emit();
796         }
797     }
798 
799     Some(ast::InlineAsm {
800         template,
801         template_strs: template_strs.into_boxed_slice(),
802         operands: args.operands,
803         clobber_abis: args.clobber_abis,
804         options: args.options,
805         line_spans,
806     })
807 }
808 
expand_asm<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> Box<dyn base::MacResult + 'cx>809 pub fn expand_asm<'cx>(
810     ecx: &'cx mut ExtCtxt<'_>,
811     sp: Span,
812     tts: TokenStream,
813 ) -> Box<dyn base::MacResult + 'cx> {
814     match parse_args(ecx, sp, tts, false) {
815         Ok(args) => {
816             let expr = if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
817                 P(ast::Expr {
818                     id: ast::DUMMY_NODE_ID,
819                     kind: ast::ExprKind::InlineAsm(P(inline_asm)),
820                     span: sp,
821                     attrs: ast::AttrVec::new(),
822                     tokens: None,
823                 })
824             } else {
825                 DummyResult::raw_expr(sp, true)
826             };
827             MacEager::expr(expr)
828         }
829         Err(mut err) => {
830             err.emit();
831             DummyResult::any(sp)
832         }
833     }
834 }
835 
expand_global_asm<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> Box<dyn base::MacResult + 'cx>836 pub fn expand_global_asm<'cx>(
837     ecx: &'cx mut ExtCtxt<'_>,
838     sp: Span,
839     tts: TokenStream,
840 ) -> Box<dyn base::MacResult + 'cx> {
841     match parse_args(ecx, sp, tts, true) {
842         Ok(args) => {
843             if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
844                 MacEager::items(smallvec![P(ast::Item {
845                     ident: Ident::empty(),
846                     attrs: Vec::new(),
847                     id: ast::DUMMY_NODE_ID,
848                     kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
849                     vis: ast::Visibility {
850                         span: sp.shrink_to_lo(),
851                         kind: ast::VisibilityKind::Inherited,
852                         tokens: None,
853                     },
854                     span: ecx.with_def_site_ctxt(sp),
855                     tokens: None,
856                 })])
857             } else {
858                 DummyResult::any(sp)
859             }
860         }
861         Err(mut err) => {
862             err.emit();
863             DummyResult::any(sp)
864         }
865     }
866 }
867