@@ -210,7 +210,17 @@ pub struct ParseError {
210
210
pub label : string:: String ,
211
211
pub span : InnerSpan ,
212
212
pub secondary_label : Option < ( string:: String , InnerSpan ) > ,
213
- pub should_be_replaced_with_positional_argument : bool ,
213
+ pub suggestion : Suggestion ,
214
+ }
215
+
216
+ pub enum Suggestion {
217
+ None ,
218
+ /// Replace inline argument with positional argument:
219
+ /// `format!("{foo.bar}")` -> `format!("{}", foo.bar)`
220
+ UsePositional ,
221
+ /// Remove `r#` from identifier:
222
+ /// `format!("{r#foo}")` -> `format!("{foo}")`
223
+ RemoveRawIdent ( InnerSpan ) ,
214
224
}
215
225
216
226
/// The parser structure for interpreting the input format string. This is
@@ -365,7 +375,7 @@ impl<'a> Parser<'a> {
365
375
label : label. into ( ) ,
366
376
span,
367
377
secondary_label : None ,
368
- should_be_replaced_with_positional_argument : false ,
378
+ suggestion : Suggestion :: None ,
369
379
} ) ;
370
380
}
371
381
@@ -389,7 +399,7 @@ impl<'a> Parser<'a> {
389
399
label : label. into ( ) ,
390
400
span,
391
401
secondary_label : None ,
392
- should_be_replaced_with_positional_argument : false ,
402
+ suggestion : Suggestion :: None ,
393
403
} ) ;
394
404
}
395
405
@@ -493,7 +503,7 @@ impl<'a> Parser<'a> {
493
503
label,
494
504
span : pos. to ( pos) ,
495
505
secondary_label,
496
- should_be_replaced_with_positional_argument : false ,
506
+ suggestion : Suggestion :: None ,
497
507
} ) ;
498
508
499
509
None
@@ -573,7 +583,37 @@ impl<'a> Parser<'a> {
573
583
Some ( ArgumentIs ( i) )
574
584
} else {
575
585
match self . cur . peek ( ) {
576
- Some ( & ( _, c) ) if rustc_lexer:: is_id_start ( c) => Some ( ArgumentNamed ( self . word ( ) ) ) ,
586
+ Some ( & ( lo, c) ) if rustc_lexer:: is_id_start ( c) => {
587
+ let word = self . word ( ) ;
588
+
589
+ // Recover from `r#ident` in format strings.
590
+ // FIXME: use a let chain
591
+ if word == "r" {
592
+ if let Some ( ( pos, '#' ) ) = self . cur . peek ( ) {
593
+ if self . input [ pos + 1 ..]
594
+ . chars ( )
595
+ . next ( )
596
+ . is_some_and ( rustc_lexer:: is_id_start)
597
+ {
598
+ self . cur . next ( ) ;
599
+ let word = self . word ( ) ;
600
+ let prefix_span = self . span ( lo, lo + 2 ) ;
601
+ let full_span = self . span ( lo, lo + 2 + word. len ( ) ) ;
602
+ self . errors . insert ( 0 , ParseError {
603
+ description : "raw identifiers are not supported" . to_owned ( ) ,
604
+ note : Some ( "identifiers in format strings can be keywords and don't need to be prefixed with `r#`" . to_string ( ) ) ,
605
+ label : "raw identifier used here" . to_owned ( ) ,
606
+ span : full_span,
607
+ secondary_label : None ,
608
+ suggestion : Suggestion :: RemoveRawIdent ( prefix_span) ,
609
+ } ) ;
610
+ return Some ( ArgumentNamed ( word) ) ;
611
+ }
612
+ }
613
+ }
614
+
615
+ Some ( ArgumentNamed ( word) )
616
+ }
577
617
578
618
// This is an `ArgumentNext`.
579
619
// Record the fact and do the resolution after parsing the
@@ -841,7 +881,7 @@ impl<'a> Parser<'a> {
841
881
label : "expected `?` to occur after `:`" . to_owned ( ) ,
842
882
span : pos. to ( pos) ,
843
883
secondary_label : None ,
844
- should_be_replaced_with_positional_argument : false ,
884
+ suggestion : Suggestion :: None ,
845
885
} ,
846
886
) ;
847
887
}
@@ -867,7 +907,7 @@ impl<'a> Parser<'a> {
867
907
label : "not supported" . to_string ( ) ,
868
908
span : InnerSpan :: new ( arg. position_span . start , field. position_span . end ) ,
869
909
secondary_label : None ,
870
- should_be_replaced_with_positional_argument : true ,
910
+ suggestion : Suggestion :: UsePositional ,
871
911
} ,
872
912
) ;
873
913
}
0 commit comments