@@ -16,7 +16,7 @@ struct AsmArgs {
1616 named_args : FxHashMap < Symbol , usize > ,
1717 reg_args : FxHashSet < usize > ,
1818 options : ast:: InlineAsmOptions ,
19- options_span : Option < Span > ,
19+ options_spans : Vec < Span > ,
2020}
2121
2222fn parse_args < ' a > (
@@ -59,7 +59,7 @@ fn parse_args<'a>(
5959 named_args : FxHashMap :: default ( ) ,
6060 reg_args : FxHashSet :: default ( ) ,
6161 options : ast:: InlineAsmOptions :: empty ( ) ,
62- options_span : None ,
62+ options_spans : vec ! [ ] ,
6363 } ;
6464
6565 let mut allow_templates = true ;
@@ -174,9 +174,9 @@ fn parse_args<'a>(
174174
175175 // Validate the order of named, positional & explicit register operands and options. We do
176176 // 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 ( ) {
178178 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" )
180180 . span_label ( span, "argument" )
181181 . emit ( ) ;
182182 }
@@ -227,23 +227,23 @@ fn parse_args<'a>(
227227 if args. options . contains ( ast:: InlineAsmOptions :: NOMEM )
228228 && args. options . contains ( ast:: InlineAsmOptions :: READONLY )
229229 {
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" )
232232 . emit ( ) ;
233233 }
234234 if args. options . contains ( ast:: InlineAsmOptions :: PURE )
235235 && args. options . contains ( ast:: InlineAsmOptions :: NORETURN )
236236 {
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" )
239239 . emit ( ) ;
240240 }
241241 if args. options . contains ( ast:: InlineAsmOptions :: PURE )
242242 && !args. options . intersects ( ast:: InlineAsmOptions :: NOMEM | ast:: InlineAsmOptions :: READONLY )
243243 {
244- let span = args. options_span . unwrap ( ) ;
244+ let spans = args. options_spans . clone ( ) ;
245245 ecx. struct_span_err (
246- span ,
246+ spans ,
247247 "the `pure` option must be combined with either `nomem` or `readonly`" ,
248248 )
249249 . emit ( ) ;
@@ -267,7 +267,7 @@ fn parse_args<'a>(
267267 }
268268 if args. options . contains ( ast:: InlineAsmOptions :: PURE ) && !have_real_output {
269269 ecx. struct_span_err (
270- args. options_span . unwrap ( ) ,
270+ args. options_spans . clone ( ) ,
271271 "asm with `pure` option must have at least one output" ,
272272 )
273273 . emit ( ) ;
@@ -283,27 +283,71 @@ fn parse_args<'a>(
283283 Ok ( args)
284284}
285285
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+
286330fn parse_options < ' a > ( p : & mut Parser < ' a > , args : & mut AsmArgs ) -> Result < ( ) , DiagnosticBuilder < ' a > > {
287331 let span_start = p. prev_token . span ;
288332
289333 p. expect ( & token:: OpenDelim ( token:: DelimToken :: Paren ) ) ?;
290334
291335 while !p. eat ( & token:: CloseDelim ( token:: DelimToken :: Paren ) ) {
292336 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 ) ;
294338 } 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 ) ;
296340 } 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 ) ;
298342 } 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 ) ;
300344 } 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 ) ;
302346 } 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 ) ;
304348 } else {
305349 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 ) ;
307351 }
308352
309353 // Allow trailing commas
@@ -314,14 +358,7 @@ fn parse_options<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> Result<(), Diagn
314358 }
315359
316360 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) ;
325362
326363 Ok ( ( ) )
327364}
0 commit comments