@@ -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,56 +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 ( ) ;
658
- return ;
609
+ if !placeholders. is_empty ( ) {
610
+ report_redundant_placeholders ( & mut diag, & args, used, placeholders) ;
659
611
}
660
612
661
613
// Used to ensure we only report translations for *one* kind of foreign format.
@@ -745,6 +697,64 @@ fn report_missing_placeholders(
745
697
diag. emit ( ) ;
746
698
}
747
699
700
+ fn report_redundant_placeholders (
701
+ diag : & mut DiagnosticBuilder < ' _ , ErrorGuaranteed > ,
702
+ args : & FormatArguments ,
703
+ used : & [ bool ] ,
704
+ placeholders : Vec < ( Span , & str ) > ,
705
+ ) {
706
+ let mut args_spans = vec ! [ ] ;
707
+ let mut fmt_spans = FxIndexSet :: default ( ) ;
708
+
709
+ for ( i, unnamed_arg) in args. unnamed_args ( ) . iter ( ) . enumerate ( ) . rev ( ) {
710
+ let Some ( ty) = unnamed_arg. expr . to_ty ( ) else { continue } ;
711
+ let Some ( argument_binding) = ty. kind . is_simple_path ( ) else { continue } ;
712
+ let argument_binding = argument_binding. as_str ( ) ;
713
+
714
+ if used[ i] {
715
+ continue ;
716
+ }
717
+
718
+ let matching_placeholders = placeholders
719
+ . iter ( )
720
+ . filter ( |( _, inline_binding) | argument_binding == * inline_binding)
721
+ . collect :: < Vec < _ > > ( ) ;
722
+
723
+ if !matching_placeholders. is_empty ( ) {
724
+ args_spans. push ( unnamed_arg. expr . span ) ;
725
+ for placeholder in & matching_placeholders {
726
+ fmt_spans. insert ( * placeholder) ;
727
+ }
728
+ }
729
+ }
730
+
731
+ if !args_spans. is_empty ( ) {
732
+ let mut multispan = MultiSpan :: from ( args_spans. clone ( ) ) ;
733
+
734
+ let msg = if fmt_spans. len ( ) > 1 {
735
+ "the formatting strings already captures the bindings \
736
+ directly, they don't need to be included in the argument list"
737
+ } else {
738
+ "the formatting string already captures the binding \
739
+ directly, it doesn't need to be included in the argument list"
740
+ } ;
741
+
742
+ for ( span, binding) in fmt_spans {
743
+ multispan. push_span_label (
744
+ * span,
745
+ format ! ( "this formatting specifier is referencing the `{binding}` binding" ) ,
746
+ ) ;
747
+ }
748
+
749
+ for span in & args_spans {
750
+ multispan. push_span_label ( * span, "this can be removed" ) ;
751
+ }
752
+
753
+ diag. span_help ( multispan, msg) ;
754
+ diag. emit ( ) ;
755
+ }
756
+ }
757
+
748
758
/// Handle invalid references to positional arguments. Output different
749
759
/// errors for the case where all arguments are positional and for when
750
760
/// there are named arguments or numbered positional arguments in the
0 commit comments