about summary refs log tree commit diff
path: root/compiler/rustc_builtin_macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_builtin_macros/src')
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs21
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs58
2 files changed, 54 insertions, 25 deletions
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 1238773d58b..c2097f0bc47 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -647,6 +647,27 @@ pub(crate) struct FormatPositionalMismatch {
 }
 
 #[derive(Diagnostic)]
+#[diag(builtin_macros_format_redundant_args)]
+pub(crate) struct FormatRedundantArgs {
+    #[primary_span]
+    pub(crate) fmt_span: Span,
+    pub(crate) n: usize,
+
+    #[note]
+    pub(crate) note: MultiSpan,
+
+    #[subdiagnostic]
+    pub(crate) sugg: FormatRedundantArgsSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(builtin_macros_suggestion, applicability = "machine-applicable")]
+pub(crate) struct FormatRedundantArgsSugg {
+    #[suggestion_part(code = "")]
+    pub(crate) spans: Vec<Span>,
+}
+
+#[derive(Diagnostic)]
 #[diag(builtin_macros_test_case_non_item)]
 pub(crate) struct TestCaseNonItem {
     #[primary_span]
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 6db551aef35..32a90b307ef 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -7,12 +7,12 @@ use rustc_ast::{
     FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount,
     FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait,
 };
-use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
-use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan, PResult, SingleLabelManySpans};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{Applicability, MultiSpan, PResult, SingleLabelManySpans};
 use rustc_expand::base::{self, *};
 use rustc_parse_format as parse;
 use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::{BytePos, ErrorGuaranteed, InnerSpan, Span};
+use rustc_span::{BytePos, InnerSpan, Span};
 
 use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
 use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId};
@@ -616,7 +616,9 @@ fn report_missing_placeholders(
         .collect::<Vec<_>>();
 
     if !placeholders.is_empty() {
-        report_redundant_placeholders(&mut diag, &args, used, placeholders);
+        report_redundant_placeholders(ecx, fmt_span, &args, used, placeholders);
+        diag.cancel();
+        return;
     }
 
     // Used to ensure we only report translations for *one* kind of foreign format.
@@ -707,13 +709,16 @@ fn report_missing_placeholders(
 }
 
 fn report_redundant_placeholders(
-    diag: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+    ecx: &mut ExtCtxt<'_>,
+    fmt_span: Span,
     args: &FormatArguments,
     used: &[bool],
     placeholders: Vec<(Span, &str)>,
 ) {
+    let mut fmt_arg_indices = vec![];
     let mut args_spans = vec![];
-    let mut fmt_spans = FxIndexSet::default();
+    let mut fmt_spans = vec![];
+    let mut bindings = vec![];
 
     for (i, unnamed_arg) in args.unnamed_args().iter().enumerate().rev() {
         let Some(ty) = unnamed_arg.expr.to_ty() else { continue };
@@ -730,36 +735,39 @@ fn report_redundant_placeholders(
             .collect::<Vec<_>>();
 
         if !matching_placeholders.is_empty() {
+            fmt_arg_indices.push(i);
             args_spans.push(unnamed_arg.expr.span);
-            for placeholder in &matching_placeholders {
-                fmt_spans.insert(*placeholder);
+            for (span, binding) in &matching_placeholders {
+                if fmt_spans.contains(span) {
+                    continue;
+                }
+                fmt_spans.push(*span);
+                bindings.push(binding);
             }
         }
     }
 
     if !args_spans.is_empty() {
-        let mut multispan = MultiSpan::from(args_spans.clone());
+        let multispan = MultiSpan::from(fmt_spans);
+        let mut suggestion_spans = vec![];
 
-        let msg = if fmt_spans.len() > 1 {
-            "the formatting strings already captures the bindings \
-            directly, they don't need to be included in the argument list"
-        } else {
-            "the formatting string already captures the binding \
-            directly, it doesn't need to be included in the argument list"
-        };
+        for (arg_span, fmt_arg_idx) in args_spans.iter().zip(fmt_arg_indices.iter()) {
+            let span = if fmt_arg_idx + 1 == args.explicit_args().len() {
+                *arg_span
+            } else {
+                arg_span.until(args.explicit_args()[*fmt_arg_idx + 1].expr.span)
+            };
 
-        for (span, binding) in fmt_spans {
-            multispan.push_span_label(
-                *span,
-                format!("this formatting specifier is referencing the `{binding}` binding"),
-            );
+            suggestion_spans.push(span);
         }
 
-        for span in &args_spans {
-            multispan.push_span_label(*span, "this can be removed");
-        }
+        let mut diag = ecx.create_err(errors::FormatRedundantArgs {
+            fmt_span,
+            note: multispan,
+            n: args_spans.len(),
+            sugg: errors::FormatRedundantArgsSugg { spans: suggestion_spans },
+        });
 
-        diag.span_help(multispan, msg);
         diag.emit();
     }
 }