@@ -16,7 +16,7 @@ struct AsmArgs {
16
16
named_args : FxHashMap < Symbol , usize > ,
17
17
reg_args : FxHashSet < usize > ,
18
18
options : ast:: InlineAsmOptions ,
19
- options_span : Option < Span > ,
19
+ options_spans : Vec < Span > ,
20
20
}
21
21
22
22
fn parse_args < ' a > (
@@ -59,7 +59,7 @@ fn parse_args<'a>(
59
59
named_args : FxHashMap :: default ( ) ,
60
60
reg_args : FxHashSet :: default ( ) ,
61
61
options : ast:: InlineAsmOptions :: empty ( ) ,
62
- options_span : None ,
62
+ options_spans : vec ! [ ] ,
63
63
} ;
64
64
65
65
let mut allow_templates = true ;
@@ -174,9 +174,9 @@ fn parse_args<'a>(
174
174
175
175
// Validate the order of named, positional & explicit register operands and options. We do
176
176
// this at the end once we have the full span of the argument available.
177
- if let Some ( options_span ) = args. options_span {
177
+ if ! args. options_spans . is_empty ( ) {
178
178
ecx. struct_span_err ( span, "arguments are not allowed after options" )
179
- . span_label ( options_span , "previous options" )
179
+ . span_labels ( args . options_spans . clone ( ) , "previous options" )
180
180
. span_label ( span, "argument" )
181
181
. emit ( ) ;
182
182
}
@@ -227,23 +227,23 @@ fn parse_args<'a>(
227
227
if args. options . contains ( ast:: InlineAsmOptions :: NOMEM )
228
228
&& args. options . contains ( ast:: InlineAsmOptions :: READONLY )
229
229
{
230
- let span = args. options_span . unwrap ( ) ;
231
- ecx. struct_span_err ( span , "the `nomem` and `readonly` options are mutually exclusive" )
230
+ let spans = args. options_spans . clone ( ) ;
231
+ ecx. struct_span_err ( spans , "the `nomem` and `readonly` options are mutually exclusive" )
232
232
. emit ( ) ;
233
233
}
234
234
if args. options . contains ( ast:: InlineAsmOptions :: PURE )
235
235
&& args. options . contains ( ast:: InlineAsmOptions :: NORETURN )
236
236
{
237
- let span = args. options_span . unwrap ( ) ;
238
- ecx. struct_span_err ( span , "the `pure` and `noreturn` options are mutually exclusive" )
237
+ let spans = args. options_spans . clone ( ) ;
238
+ ecx. struct_span_err ( spans , "the `pure` and `noreturn` options are mutually exclusive" )
239
239
. emit ( ) ;
240
240
}
241
241
if args. options . contains ( ast:: InlineAsmOptions :: PURE )
242
242
&& !args. options . intersects ( ast:: InlineAsmOptions :: NOMEM | ast:: InlineAsmOptions :: READONLY )
243
243
{
244
- let span = args. options_span . unwrap ( ) ;
244
+ let spans = args. options_spans . clone ( ) ;
245
245
ecx. struct_span_err (
246
- span ,
246
+ spans ,
247
247
"the `pure` option must be combined with either `nomem` or `readonly`" ,
248
248
)
249
249
. emit ( ) ;
@@ -267,7 +267,7 @@ fn parse_args<'a>(
267
267
}
268
268
if args. options . contains ( ast:: InlineAsmOptions :: PURE ) && !have_real_output {
269
269
ecx. struct_span_err (
270
- args. options_span . unwrap ( ) ,
270
+ args. options_spans . clone ( ) ,
271
271
"asm with `pure` option must have at least one output" ,
272
272
)
273
273
. emit ( ) ;
@@ -283,27 +283,71 @@ fn parse_args<'a>(
283
283
Ok ( args)
284
284
}
285
285
286
+ /// Report a duplicate option error.
287
+ ///
288
+ /// This function must be called immediately after the option token is parsed.
289
+ /// Otherwise, the suggestion will be incorrect.
290
+ fn err_duplicate_option < ' a > ( p : & mut Parser < ' a > , symbol : Symbol , span : Span ) {
291
+ let mut err = p
292
+ . sess
293
+ . span_diagnostic
294
+ . struct_span_err ( span, & format ! ( "the `{}` option was already provided" , symbol) ) ;
295
+ err. span_label ( span, "this option was already provided" ) ;
296
+
297
+ // Tool-only output
298
+ let mut full_span = span;
299
+ if p. token . kind == token:: Comma {
300
+ full_span = full_span. to ( p. token . span ) ;
301
+ }
302
+ err. tool_only_span_suggestion (
303
+ full_span,
304
+ "remove this option" ,
305
+ String :: new ( ) ,
306
+ Applicability :: MachineApplicable ,
307
+ ) ;
308
+
309
+ err. emit ( ) ;
310
+ }
311
+
312
+ /// Try to set the provided option in the provided `AsmArgs`.
313
+ /// If it is already set, report a duplicate option error.
314
+ ///
315
+ /// This function must be called immediately after the option token is parsed.
316
+ /// Otherwise, the error will not point to the correct spot.
317
+ fn try_set_option < ' a > (
318
+ p : & mut Parser < ' a > ,
319
+ args : & mut AsmArgs ,
320
+ symbol : Symbol ,
321
+ option : ast:: InlineAsmOptions ,
322
+ ) {
323
+ if !args. options . contains ( option) {
324
+ args. options |= option;
325
+ } else {
326
+ err_duplicate_option ( p, symbol, p. prev_token . span ) ;
327
+ }
328
+ }
329
+
286
330
fn parse_options < ' a > ( p : & mut Parser < ' a > , args : & mut AsmArgs ) -> Result < ( ) , DiagnosticBuilder < ' a > > {
287
331
let span_start = p. prev_token . span ;
288
332
289
333
p. expect ( & token:: OpenDelim ( token:: DelimToken :: Paren ) ) ?;
290
334
291
335
while !p. eat ( & token:: CloseDelim ( token:: DelimToken :: Paren ) ) {
292
336
if p. eat ( & token:: Ident ( sym:: pure, false ) ) {
293
- args. options |= ast:: InlineAsmOptions :: PURE ;
337
+ try_set_option ( p , args, sym :: pure , ast:: InlineAsmOptions :: PURE ) ;
294
338
} else if p. eat ( & token:: Ident ( sym:: nomem, false ) ) {
295
- args. options |= ast:: InlineAsmOptions :: NOMEM ;
339
+ try_set_option ( p , args, sym :: nomem , ast:: InlineAsmOptions :: NOMEM ) ;
296
340
} else if p. eat ( & token:: Ident ( sym:: readonly, false ) ) {
297
- args. options |= ast:: InlineAsmOptions :: READONLY ;
341
+ try_set_option ( p , args, sym :: readonly , ast:: InlineAsmOptions :: READONLY ) ;
298
342
} else if p. eat ( & token:: Ident ( sym:: preserves_flags, false ) ) {
299
- args. options |= ast:: InlineAsmOptions :: PRESERVES_FLAGS ;
343
+ try_set_option ( p , args, sym :: preserves_flags , ast:: InlineAsmOptions :: PRESERVES_FLAGS ) ;
300
344
} else if p. eat ( & token:: Ident ( sym:: noreturn, false ) ) {
301
- args. options |= ast:: InlineAsmOptions :: NORETURN ;
345
+ try_set_option ( p , args, sym :: noreturn , ast:: InlineAsmOptions :: NORETURN ) ;
302
346
} else if p. eat ( & token:: Ident ( sym:: nostack, false ) ) {
303
- args. options |= ast:: InlineAsmOptions :: NOSTACK ;
347
+ try_set_option ( p , args, sym :: nostack , ast:: InlineAsmOptions :: NOSTACK ) ;
304
348
} else {
305
349
p. expect ( & token:: Ident ( sym:: att_syntax, false ) ) ?;
306
- args. options |= ast:: InlineAsmOptions :: ATT_SYNTAX ;
350
+ try_set_option ( p , args, sym :: att_syntax , ast:: InlineAsmOptions :: ATT_SYNTAX ) ;
307
351
}
308
352
309
353
// Allow trailing commas
@@ -314,14 +358,7 @@ fn parse_options<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> Result<(), Diagn
314
358
}
315
359
316
360
let new_span = span_start. to ( p. prev_token . span ) ;
317
- if let Some ( options_span) = args. options_span {
318
- p. struct_span_err ( new_span, "asm options cannot be specified multiple times" )
319
- . span_label ( options_span, "previously here" )
320
- . span_label ( new_span, "duplicate options" )
321
- . emit ( ) ;
322
- } else {
323
- args. options_span = Some ( new_span) ;
324
- }
361
+ args. options_spans . push ( new_span) ;
325
362
326
363
Ok ( ( ) )
327
364
}
0 commit comments