@@ -5,8 +5,8 @@ use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma};
55use clippy_utils:: { is_in_test, sym} ;
66use rustc_ast:: token:: LitKind ;
77use rustc_ast:: {
8- FormatArgPosition , FormatArgPositionKind , FormatArgs , FormatArgsPiece , FormatOptions , FormatPlaceholder ,
9- FormatTrait ,
8+ FormatArgPosition , FormatArgPositionKind , FormatArgs , FormatArgsPiece , FormatCount , FormatOptions ,
9+ FormatPlaceholder , FormatTrait ,
1010} ;
1111use rustc_errors:: Applicability ;
1212use rustc_hir:: { Expr , Impl , Item , ItemKind } ;
@@ -556,12 +556,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
556556 // Decrement the index of the remaining by the number of replaced positional arguments
557557 if !suggestion. is_empty ( ) {
558558 for piece in & format_args. template {
559- if let Some ( ( span, index) ) = positional_arg_piece_span ( piece)
560- && suggestion. iter ( ) . all ( |( s, _) | * s != span)
561- {
562- let decrement = replaced_position. iter ( ) . filter ( |i| * * i < index) . count ( ) ;
563- suggestion. push ( ( span, format ! ( "{{{}}}" , index. saturating_sub( decrement) ) ) ) ;
564- }
559+ relocalize_format_args_indexes ( piece, & mut suggestion, & replaced_position) ;
565560 }
566561 }
567562
@@ -574,7 +569,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
574569 }
575570}
576571
577- /// Extract Span and its index from the given `piece`, iff it's positional argument.
572+ /// Extract Span and its index from the given `piece`, if it's positional argument.
578573fn positional_arg_piece_span ( piece : & FormatArgsPiece ) -> Option < ( Span , usize ) > {
579574 match piece {
580575 FormatArgsPiece :: Placeholder ( FormatPlaceholder {
@@ -591,6 +586,57 @@ fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
591586 }
592587}
593588
589+ /// Relocalizes the indexes of positional arguments in the format string
590+ fn relocalize_format_args_indexes (
591+ piece : & FormatArgsPiece ,
592+ suggestion : & mut Vec < ( Span , String ) > ,
593+ replaced_position : & [ usize ] ,
594+ ) {
595+ if let FormatArgsPiece :: Placeholder ( FormatPlaceholder {
596+ argument :
597+ FormatArgPosition {
598+ index : Ok ( index) ,
599+ // Only consider positional arguments
600+ kind : FormatArgPositionKind :: Number ,
601+ span : Some ( span) ,
602+ } ,
603+ format_options,
604+ ..
605+ } ) = piece
606+ {
607+ if suggestion. iter ( ) . any ( |( s, _) | s. overlaps ( * span) ) {
608+ // If the span is already in the suggestion, we don't need to process it again
609+ return ;
610+ }
611+
612+ // lambda to get the decremented index based on the replaced positions
613+ let decremented_index = |index : usize | -> usize {
614+ let decrement = replaced_position. iter ( ) . filter ( |& & i| i < index) . count ( ) ;
615+ index - decrement
616+ } ;
617+
618+ suggestion. push ( ( * span, decremented_index ( * index) . to_string ( ) ) ) ;
619+
620+ // If there are format options, we need to handle them as well
621+ if * format_options != FormatOptions :: default ( ) {
622+ // lambda to process width and precision format counts and add them to the suggestion
623+ let mut process_format_count = |count : & Option < FormatCount > , formatter : & dyn Fn ( usize ) -> String | {
624+ if let Some ( FormatCount :: Argument ( FormatArgPosition {
625+ index : Ok ( format_arg_index) ,
626+ kind : FormatArgPositionKind :: Number ,
627+ span : Some ( format_arg_span) ,
628+ } ) ) = count
629+ {
630+ suggestion. push ( ( * format_arg_span, formatter ( decremented_index ( * format_arg_index) ) ) ) ;
631+ }
632+ } ;
633+
634+ process_format_count ( & format_options. width , & |index : usize | format ! ( "{index}$" ) ) ;
635+ process_format_count ( & format_options. precision , & |index : usize | format ! ( ".{index}$" ) ) ;
636+ }
637+ }
638+ }
639+
594640/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
595641///
596642/// `r#"a"#` -> (`a`, true)
0 commit comments