@@ -8,11 +8,11 @@ use rustc_ast::{
88 FormatDebugHex , FormatOptions , FormatPlaceholder , FormatSign , FormatTrait ,
99} ;
1010use rustc_data_structures:: fx:: { FxHashSet , FxIndexSet } ;
11- use rustc_errors:: { Applicability , MultiSpan , PResult , SingleLabelManySpans } ;
11+ use rustc_errors:: { Applicability , DiagnosticBuilder , MultiSpan , PResult , SingleLabelManySpans } ;
1212use rustc_expand:: base:: { self , * } ;
1313use rustc_parse_format as parse;
1414use rustc_span:: symbol:: { Ident , Symbol } ;
15- use rustc_span:: { BytePos , InnerSpan , Span } ;
15+ use rustc_span:: { BytePos , ErrorGuaranteed , InnerSpan , Span } ;
1616
1717use rustc_lint_defs:: builtin:: NAMED_ARGUMENTS_USED_POSITIONALLY ;
1818use rustc_lint_defs:: { BufferedEarlyLint , BuiltinLintDiagnostics , LintId } ;
@@ -606,55 +606,8 @@ fn report_missing_placeholders(
606606 } )
607607 . collect :: < Vec < _ > > ( ) ;
608608
609- let mut args_spans = vec ! [ ] ;
610- let mut fmt_spans = FxIndexSet :: default ( ) ;
611-
612- for ( i, unnamed_arg) in args. unnamed_args ( ) . iter ( ) . enumerate ( ) . rev ( ) {
613- let Some ( ty) = unnamed_arg. expr . to_ty ( ) else { continue } ;
614- let Some ( argument_binding) = ty. kind . is_simple_path ( ) else { continue } ;
615- let argument_binding = argument_binding. as_str ( ) ;
616-
617- if used[ i] {
618- continue ;
619- }
620-
621- let matching_placeholders = placeholders
622- . iter ( )
623- . filter ( |( _, inline_binding) | argument_binding == * inline_binding)
624- . collect :: < Vec < _ > > ( ) ;
625-
626- if !matching_placeholders. is_empty ( ) {
627- args_spans. push ( unnamed_arg. expr . span ) ;
628- for placeholder in & matching_placeholders {
629- fmt_spans. insert ( * placeholder) ;
630- }
631- }
632- }
633-
634- if !args_spans. is_empty ( ) {
635- let mut multispan = MultiSpan :: from ( args_spans. clone ( ) ) ;
636-
637- let msg = if fmt_spans. len ( ) > 1 {
638- "the formatting strings already captures the bindings \
639- directly, they don't need to be included in the argument list"
640- } else {
641- "the formatting string already captures the binding \
642- directly, it doesn't need to be included in the argument list"
643- } ;
644-
645- for ( span, binding) in fmt_spans {
646- multispan. push_span_label (
647- * span,
648- format ! ( "this formatting specifier is referencing the `{binding}` binding" ) ,
649- ) ;
650- }
651-
652- for span in & args_spans {
653- multispan. push_span_label ( * span, "this can be removed" ) ;
654- }
655-
656- diag. span_help ( multispan, msg) ;
657- diag. emit ( ) ;
609+ if !placeholders. is_empty ( ) {
610+ report_redundant_placeholders ( diag, & args, used, placeholders) ;
658611 return ;
659612 }
660613
@@ -745,6 +698,64 @@ fn report_missing_placeholders(
745698 diag. emit ( ) ;
746699}
747700
701+ fn report_redundant_placeholders (
702+ mut diag : DiagnosticBuilder < ' _ , ErrorGuaranteed > ,
703+ args : & FormatArguments ,
704+ used : & [ bool ] ,
705+ placeholders : Vec < ( Span , & str ) > ,
706+ ) {
707+ let mut args_spans = vec ! [ ] ;
708+ let mut fmt_spans = FxIndexSet :: default ( ) ;
709+
710+ for ( i, unnamed_arg) in args. unnamed_args ( ) . iter ( ) . enumerate ( ) . rev ( ) {
711+ let Some ( ty) = unnamed_arg. expr . to_ty ( ) else { continue } ;
712+ let Some ( argument_binding) = ty. kind . is_simple_path ( ) else { continue } ;
713+ let argument_binding = argument_binding. as_str ( ) ;
714+
715+ if used[ i] {
716+ continue ;
717+ }
718+
719+ let matching_placeholders = placeholders
720+ . iter ( )
721+ . filter ( |( _, inline_binding) | argument_binding == * inline_binding)
722+ . collect :: < Vec < _ > > ( ) ;
723+
724+ if !matching_placeholders. is_empty ( ) {
725+ args_spans. push ( unnamed_arg. expr . span ) ;
726+ for placeholder in & matching_placeholders {
727+ fmt_spans. insert ( * placeholder) ;
728+ }
729+ }
730+ }
731+
732+ if !args_spans. is_empty ( ) {
733+ let mut multispan = MultiSpan :: from ( args_spans. clone ( ) ) ;
734+
735+ let msg = if fmt_spans. len ( ) > 1 {
736+ "the formatting strings already captures the bindings \
737+ directly, they don't need to be included in the argument list"
738+ } else {
739+ "the formatting string already captures the binding \
740+ directly, it doesn't need to be included in the argument list"
741+ } ;
742+
743+ for ( span, binding) in fmt_spans {
744+ multispan. push_span_label (
745+ * span,
746+ format ! ( "this formatting specifier is referencing the `{binding}` binding" ) ,
747+ ) ;
748+ }
749+
750+ for span in & args_spans {
751+ multispan. push_span_label ( * span, "this can be removed" ) ;
752+ }
753+
754+ diag. span_help ( multispan, msg) ;
755+ diag. emit ( ) ;
756+ }
757+ }
758+
748759/// Handle invalid references to positional arguments. Output different
749760/// errors for the case where all arguments are positional and for when
750761/// there are named arguments or numbered positional arguments in the
0 commit comments