@@ -8,11 +8,11 @@ use rustc_ast::{
8
8
FormatDebugHex , FormatOptions , FormatPlaceholder , FormatSign , FormatTrait ,
9
9
} ;
10
10
use rustc_data_structures:: fx:: { FxHashSet , FxIndexSet } ;
11
- use rustc_errors:: { Applicability , MultiSpan , PResult , SingleLabelManySpans } ;
11
+ use rustc_errors:: { Applicability , DiagnosticBuilder , MultiSpan , PResult , SingleLabelManySpans } ;
12
12
use rustc_expand:: base:: { self , * } ;
13
13
use rustc_parse_format as parse;
14
14
use rustc_span:: symbol:: { Ident , Symbol } ;
15
- use rustc_span:: { BytePos , InnerSpan , Span } ;
15
+ use rustc_span:: { BytePos , ErrorGuaranteed , InnerSpan , Span } ;
16
16
17
17
use rustc_lint_defs:: builtin:: NAMED_ARGUMENTS_USED_POSITIONALLY ;
18
18
use rustc_lint_defs:: { BufferedEarlyLint , BuiltinLintDiagnostics , LintId } ;
@@ -606,55 +606,8 @@ fn report_missing_placeholders(
606
606
} )
607
607
. collect :: < Vec < _ > > ( ) ;
608
608
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) ;
658
611
return ;
659
612
}
660
613
@@ -745,6 +698,64 @@ fn report_missing_placeholders(
745
698
diag. emit ( ) ;
746
699
}
747
700
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
+
748
759
/// Handle invalid references to positional arguments. Output different
749
760
/// errors for the case where all arguments are positional and for when
750
761
/// there are named arguments or numbered positional arguments in the
0 commit comments