1
1
use std:: borrow:: Cow ;
2
- use std:: ops:: Range ;
2
+ use std:: iter;
3
+ use std:: ops:: { Deref , Range } ;
3
4
4
5
use crate :: utils:: { snippet_opt, snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then} ;
5
- use rustc_ast:: ast:: { Expr , ExprKind , ImplKind , Item , ItemKind , LitKind , MacCall , Path , StrLit , StrStyle } ;
6
- use rustc_ast:: token;
6
+ use rustc_ast:: ast:: { Expr , ExprKind , ImplKind , Item , ItemKind , MacCall , Path , StrLit , StrStyle } ;
7
+ use rustc_ast:: token:: { self , LitKind } ;
7
8
use rustc_ast:: tokenstream:: TokenStream ;
8
9
use rustc_errors:: Applicability ;
9
10
use rustc_lexer:: unescape:: { self , EscapeError } ;
@@ -437,7 +438,7 @@ impl Write {
437
438
fn parse_fmt_string ( & self , cx : & EarlyContext < ' _ > , str : & StrLit ) -> Option < SimpleFormatArgs > {
438
439
use rustc_parse_format:: { ParseMode , Parser , Piece } ;
439
440
440
- let str_sym = str. symbol . as_str ( ) ;
441
+ let str_sym = str. symbol_unescaped . as_str ( ) ;
441
442
let style = match str. style {
442
443
StrStyle :: Cooked => None ,
443
444
StrStyle :: Raw ( n) => Some ( n as usize ) ,
@@ -513,21 +514,17 @@ impl Write {
513
514
if !parser. eat ( & token:: Comma ) {
514
515
return ( Some ( fmtstr) , expr) ;
515
516
}
517
+
518
+ let comma_span = parser. prev_token . span ;
516
519
let token_expr = if let Ok ( expr) = parser. parse_expr ( ) . map_err ( |mut err| err. cancel ( ) ) {
517
520
expr
518
521
} else {
519
522
return ( Some ( fmtstr) , None ) ;
520
523
} ;
521
- let ( fmt_spans, span) = match & token_expr. kind {
522
- ExprKind :: Lit ( lit) if !matches ! ( lit. kind, LitKind :: Int ( ..) | LitKind :: Float ( ..) ) => {
523
- ( unnamed_args. next ( ) . unwrap_or ( & [ ] ) , token_expr. span )
524
- } ,
524
+ let ( fmt_spans, lit) = match & token_expr. kind {
525
+ ExprKind :: Lit ( lit) => ( unnamed_args. next ( ) . unwrap_or ( & [ ] ) , lit) ,
525
526
ExprKind :: Assign ( lhs, rhs, _) => match ( & lhs. kind , & rhs. kind ) {
526
- ( ExprKind :: Path ( _, p) , ExprKind :: Lit ( lit) )
527
- if !matches ! ( lit. kind, LitKind :: Int ( ..) | LitKind :: Float ( ..) ) =>
528
- {
529
- ( args. get_named ( p) , rhs. span )
530
- } ,
527
+ ( ExprKind :: Path ( _, p) , ExprKind :: Lit ( lit) ) => ( args. get_named ( p) , lit) ,
531
528
_ => continue ,
532
529
} ,
533
530
_ => {
@@ -536,8 +533,45 @@ impl Write {
536
533
} ,
537
534
} ;
538
535
536
+ let replacement: String = match lit. token . kind {
537
+ LitKind :: Integer | LitKind :: Float | LitKind :: Err => continue ,
538
+ LitKind :: StrRaw ( _) | LitKind :: ByteStrRaw ( _) if matches ! ( fmtstr. style, StrStyle :: Raw ( _) ) => {
539
+ lit. token . symbol . as_str ( ) . replace ( "{" , "{{" ) . replace ( "}" , "}}" )
540
+ } ,
541
+ LitKind :: Str | LitKind :: ByteStr if matches ! ( fmtstr. style, StrStyle :: Cooked ) => {
542
+ lit. token . symbol . as_str ( ) . replace ( "{" , "{{" ) . replace ( "}" , "}}" )
543
+ } ,
544
+ LitKind :: StrRaw ( _) | LitKind :: Str | LitKind :: ByteStrRaw ( _) | LitKind :: ByteStr => continue ,
545
+ LitKind :: Byte | LitKind :: Char => match lit. token . symbol . as_str ( ) . deref ( ) {
546
+ "\" " if matches ! ( fmtstr. style, StrStyle :: Cooked ) => "\\ \" " ,
547
+ "\" " if matches ! ( fmtstr. style, StrStyle :: Raw ( 0 ) ) => continue ,
548
+ "\\ \\ " if matches ! ( fmtstr. style, StrStyle :: Raw ( _) ) => "\\ " ,
549
+ "\\ '" => "'" ,
550
+ "{" => "{{" ,
551
+ "}" => "}}" ,
552
+ x if matches ! ( fmtstr. style, StrStyle :: Raw ( _) ) && x. starts_with ( "\\ " ) => continue ,
553
+ x => x,
554
+ }
555
+ . into ( ) ,
556
+ LitKind :: Bool => lit. token . symbol . as_str ( ) . deref ( ) . into ( ) ,
557
+ } ;
558
+
539
559
if !fmt_spans. is_empty ( ) {
540
- span_lint ( cx, lint, span, "literal with an empty format string" ) ;
560
+ span_lint_and_then (
561
+ cx,
562
+ lint,
563
+ token_expr. span ,
564
+ "literal with an empty format string" ,
565
+ |diag| {
566
+ diag. multipart_suggestion (
567
+ "try this" ,
568
+ iter:: once ( ( comma_span. to ( token_expr. span ) , String :: new ( ) ) )
569
+ . chain ( fmt_spans. iter ( ) . cloned ( ) . zip ( iter:: repeat ( replacement) ) )
570
+ . collect ( ) ,
571
+ Applicability :: MachineApplicable ,
572
+ ) ;
573
+ } ,
574
+ ) ;
541
575
}
542
576
}
543
577
}
0 commit comments