@@ -407,8 +407,8 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
407407 // we cannot remove any other arguments in the format string,
408408 // because the index numbers might be wrong after inlining.
409409 // Example of an un-inlinable format: print!("{}{1}", foo, 2)
410- for ( pos, usage) in self . format_arg_positions ( ) {
411- if !self . check_one_arg ( pos, usage, & mut fixes) {
410+ for ( placeholder , pos, usage) in self . format_arg_positions ( ) {
411+ if !self . check_one_arg ( placeholder , pos, usage, & mut fixes) {
412412 return ;
413413 }
414414 }
@@ -443,31 +443,64 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
443443 ) ;
444444 }
445445
446- fn check_one_arg ( & self , pos : & FormatArgPosition , usage : FormatParamUsage , fixes : & mut Vec < ( Span , String ) > ) -> bool {
446+ fn check_one_arg (
447+ & self ,
448+ placeholder : & FormatPlaceholder ,
449+ pos : & FormatArgPosition ,
450+ usage : FormatParamUsage ,
451+ fixes : & mut Vec < ( Span , String ) > ,
452+ ) -> bool {
447453 let index = pos. index . unwrap ( ) ;
448454 let arg = & self . format_args . arguments . all_args ( ) [ index] ;
449455
450- if !matches ! ( arg. kind, FormatArgumentKind :: Captured ( _) )
451- && let rustc_ast:: ExprKind :: Path ( None , path) = & arg. expr . kind
452- && let [ segment] = path. segments . as_slice ( )
453- && segment. args . is_none ( )
454- && let Some ( arg_span) = format_arg_removal_span ( self . format_args , index)
455- && let Some ( pos_span) = pos. span
456- {
457- let replacement = match usage {
458- FormatParamUsage :: Argument => segment. ident . name . to_string ( ) ,
459- FormatParamUsage :: Width => format ! ( "{}$" , segment. ident. name) ,
460- FormatParamUsage :: Precision => format ! ( ".{}$" , segment. ident. name) ,
461- } ;
462- fixes. push ( ( pos_span, replacement) ) ;
463- fixes. push ( ( arg_span, String :: new ( ) ) ) ;
464- true // successful inlining, continue checking
465- } else {
456+ if matches ! ( arg. kind, FormatArgumentKind :: Captured ( _) ) {
466457 // Do not continue inlining (return false) in case
467458 // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
468459 // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
469460 pos. kind != FormatArgPositionKind :: Number
470461 && ( !self . ignore_mixed || matches ! ( arg. kind, FormatArgumentKind :: Captured ( _) ) )
462+ } else {
463+ match & arg. expr . kind {
464+ rustc_ast:: ExprKind :: Path ( None , path)
465+ if let [ segment] = path. segments . as_slice ( )
466+ && segment. args . is_none ( )
467+ && let Some ( arg_span) = format_arg_removal_span ( self . format_args , index)
468+ && let Some ( pos_span) = pos. span =>
469+ {
470+ let replacement = match usage {
471+ FormatParamUsage :: Argument => segment. ident . name . to_string ( ) ,
472+ FormatParamUsage :: Width => format ! ( "{}$" , segment. ident. name) ,
473+ FormatParamUsage :: Precision => format ! ( ".{}$" , segment. ident. name) ,
474+ } ;
475+ fixes. push ( ( pos_span, replacement) ) ;
476+ fixes. push ( ( arg_span, String :: new ( ) ) ) ;
477+ true // successful inlining, continue checking
478+ } ,
479+ rustc_ast:: ExprKind :: Lit ( lit)
480+ if matches ! (
481+ lit. kind,
482+ rustc_ast:: token:: LitKind :: Str | rustc_ast:: token:: LitKind :: Char
483+ ) && matches ! ( usage, FormatParamUsage :: Argument )
484+ // Only inline string and char literals when there are no format options
485+ && placeholder. format_options == FormatOptions :: default ( )
486+ && let Some ( pos_span) = pos. span
487+ && let Some ( arg_span) = format_arg_removal_span ( self . format_args , index)
488+ && arg. expr . span . eq_ctxt ( arg_span) =>
489+ {
490+ // Skip the surrounding `{` and `}` when replacing
491+ let pos_span = pos_span
492+ . with_lo ( pos_span. lo ( ) - rustc_span:: BytePos ( 1 ) )
493+ . with_hi ( pos_span. hi ( ) + rustc_span:: BytePos ( 1 ) ) ;
494+ let replacement = lit. symbol . to_string ( ) ;
495+ fixes. push ( ( pos_span, replacement) ) ;
496+ fixes. push ( ( arg_span, String :: new ( ) ) ) ;
497+ true
498+ } ,
499+ _ => {
500+ pos. kind != FormatArgPositionKind :: Number
501+ && ( !self . ignore_mixed || matches ! ( arg. kind, FormatArgumentKind :: Captured ( _) ) )
502+ } ,
503+ }
471504 }
472505 }
473506
@@ -578,17 +611,17 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
578611 }
579612 }
580613
581- fn format_arg_positions ( & self ) -> impl Iterator < Item = ( & FormatArgPosition , FormatParamUsage ) > {
614+ fn format_arg_positions ( & self ) -> impl Iterator < Item = ( & FormatPlaceholder , & FormatArgPosition , FormatParamUsage ) > {
582615 self . format_args . template . iter ( ) . flat_map ( |piece| match piece {
583616 FormatArgsPiece :: Placeholder ( placeholder) => {
584617 let mut positions = ArrayVec :: < _ , 3 > :: new ( ) ;
585618
586- positions. push ( ( & placeholder. argument , FormatParamUsage :: Argument ) ) ;
619+ positions. push ( ( placeholder , & placeholder. argument , FormatParamUsage :: Argument ) ) ;
587620 if let Some ( FormatCount :: Argument ( position) ) = & placeholder. format_options . width {
588- positions. push ( ( position, FormatParamUsage :: Width ) ) ;
621+ positions. push ( ( placeholder , position, FormatParamUsage :: Width ) ) ;
589622 }
590623 if let Some ( FormatCount :: Argument ( position) ) = & placeholder. format_options . precision {
591- positions. push ( ( position, FormatParamUsage :: Precision ) ) ;
624+ positions. push ( ( placeholder , position, FormatParamUsage :: Precision ) ) ;
592625 }
593626
594627 positions
@@ -600,7 +633,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
600633 /// Returns true if the format argument at `index` is referred to by multiple format params
601634 fn is_aliased ( & self , index : usize ) -> bool {
602635 self . format_arg_positions ( )
603- . filter ( |( position, _) | position. index == Ok ( index) )
636+ . filter ( |( _ , position, _) | position. index == Ok ( index) )
604637 . at_most_one ( )
605638 . is_err ( )
606639 }
0 commit comments