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