@@ -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 } ;
@@ -615,56 +615,8 @@ fn report_missing_placeholders(
615615 } )
616616 . collect :: < Vec < _ > > ( ) ;
617617
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) ;
668620 }
669621
670622 // Used to ensure we only report translations for *one* kind of foreign format.
@@ -754,6 +706,64 @@ fn report_missing_placeholders(
754706 diag. emit ( ) ;
755707}
756708
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+
757767/// Handle invalid references to positional arguments. Output different
758768/// errors for the case where all arguments are positional and for when
759769/// there are named arguments or numbered positional arguments in the
0 commit comments