@@ -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 } ;
@@ -615,56 +615,8 @@ fn report_missing_placeholders(
615
615
} )
616
616
. collect :: < Vec < _ > > ( ) ;
617
617
618
- let mut args_spans = vec ! [ ] ;
619
- let mut fmt_spans = FxIndexSet :: default ( ) ;
620
-
621
- for ( i, unnamed_arg) in args. unnamed_args ( ) . iter ( ) . enumerate ( ) . rev ( ) {
622
- let Some ( ty) = unnamed_arg. expr . to_ty ( ) else { continue } ;
623
- let Some ( argument_binding) = ty. kind . is_simple_path ( ) else { continue } ;
624
- let argument_binding = argument_binding. as_str ( ) ;
625
-
626
- if used[ i] {
627
- continue ;
628
- }
629
-
630
- let matching_placeholders = placeholders
631
- . iter ( )
632
- . filter ( |( _, inline_binding) | argument_binding == * inline_binding)
633
- . collect :: < Vec < _ > > ( ) ;
634
-
635
- if !matching_placeholders. is_empty ( ) {
636
- args_spans. push ( unnamed_arg. expr . span ) ;
637
- for placeholder in & matching_placeholders {
638
- fmt_spans. insert ( * placeholder) ;
639
- }
640
- }
641
- }
642
-
643
- if !args_spans. is_empty ( ) {
644
- let mut multispan = MultiSpan :: from ( args_spans. clone ( ) ) ;
645
-
646
- let msg = if fmt_spans. len ( ) > 1 {
647
- "the formatting strings already captures the bindings \
648
- directly, they don't need to be included in the argument list"
649
- } else {
650
- "the formatting string already captures the binding \
651
- directly, it doesn't need to be included in the argument list"
652
- } ;
653
-
654
- for ( span, binding) in fmt_spans {
655
- multispan. push_span_label (
656
- * span,
657
- format ! ( "this formatting specifier is referencing the `{binding}` binding" ) ,
658
- ) ;
659
- }
660
-
661
- for span in & args_spans {
662
- multispan. push_span_label ( * span, "this can be removed" ) ;
663
- }
664
-
665
- diag. span_help ( multispan, msg) ;
666
- diag. emit ( ) ;
667
- return ;
618
+ if !placeholders. is_empty ( ) {
619
+ report_redundant_placeholders ( & mut diag, & args, used, placeholders) ;
668
620
}
669
621
670
622
// Used to ensure we only report translations for *one* kind of foreign format.
@@ -754,6 +706,64 @@ fn report_missing_placeholders(
754
706
diag. emit ( ) ;
755
707
}
756
708
709
+ fn report_redundant_placeholders (
710
+ diag : & mut DiagnosticBuilder < ' _ , ErrorGuaranteed > ,
711
+ args : & FormatArguments ,
712
+ used : & [ bool ] ,
713
+ placeholders : Vec < ( Span , & str ) > ,
714
+ ) {
715
+ let mut args_spans = vec ! [ ] ;
716
+ let mut fmt_spans = FxIndexSet :: default ( ) ;
717
+
718
+ for ( i, unnamed_arg) in args. unnamed_args ( ) . iter ( ) . enumerate ( ) . rev ( ) {
719
+ let Some ( ty) = unnamed_arg. expr . to_ty ( ) else { continue } ;
720
+ let Some ( argument_binding) = ty. kind . is_simple_path ( ) else { continue } ;
721
+ let argument_binding = argument_binding. as_str ( ) ;
722
+
723
+ if used[ i] {
724
+ continue ;
725
+ }
726
+
727
+ let matching_placeholders = placeholders
728
+ . iter ( )
729
+ . filter ( |( _, inline_binding) | argument_binding == * inline_binding)
730
+ . collect :: < Vec < _ > > ( ) ;
731
+
732
+ if !matching_placeholders. is_empty ( ) {
733
+ args_spans. push ( unnamed_arg. expr . span ) ;
734
+ for placeholder in & matching_placeholders {
735
+ fmt_spans. insert ( * placeholder) ;
736
+ }
737
+ }
738
+ }
739
+
740
+ if !args_spans. is_empty ( ) {
741
+ let mut multispan = MultiSpan :: from ( args_spans. clone ( ) ) ;
742
+
743
+ let msg = if fmt_spans. len ( ) > 1 {
744
+ "the formatting strings already captures the bindings \
745
+ directly, they don't need to be included in the argument list"
746
+ } else {
747
+ "the formatting string already captures the binding \
748
+ directly, it doesn't need to be included in the argument list"
749
+ } ;
750
+
751
+ for ( span, binding) in fmt_spans {
752
+ multispan. push_span_label (
753
+ * span,
754
+ format ! ( "this formatting specifier is referencing the `{binding}` binding" ) ,
755
+ ) ;
756
+ }
757
+
758
+ for span in & args_spans {
759
+ multispan. push_span_label ( * span, "this can be removed" ) ;
760
+ }
761
+
762
+ diag. span_help ( multispan, msg) ;
763
+ diag. emit ( ) ;
764
+ }
765
+ }
766
+
757
767
/// Handle invalid references to positional arguments. Output different
758
768
/// errors for the case where all arguments are positional and for when
759
769
/// there are named arguments or numbered positional arguments in the
0 commit comments