From 93df9e6d7d54160cfd4870339b5ae6568918af66 Mon Sep 17 00:00:00 2001 From: francorbacho Date: Mon, 28 Aug 2023 18:37:03 +0200 Subject: [PATCH 01/12] Suggest removing redundant arguments in format!() --- compiler/rustc_builtin_macros/src/format.rs | 84 ++++++++++++++++++++- tests/ui/did_you_mean/issue-105225.rs | 19 +++++ tests/ui/did_you_mean/issue-105225.stderr | 81 ++++++++++++++++++++ 3 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 tests/ui/did_you_mean/issue-105225.rs create mode 100644 tests/ui/did_you_mean/issue-105225.stderr diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 8397b5e4221..fd286391437 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1,3 +1,4 @@ +use parse::Position::ArgumentNamed; use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{token, StmtKind}; @@ -6,7 +7,7 @@ FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount, FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, }; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::{Applicability, MultiSpan, PResult, SingleLabelManySpans}; use rustc_expand::base::{self, *}; use rustc_parse_format as parse; @@ -357,8 +358,8 @@ enum ArgRef<'a> { let mut unfinished_literal = String::new(); let mut placeholder_index = 0; - for piece in pieces { - match piece { + for piece in &pieces { + match *piece { parse::Piece::String(s) => { unfinished_literal.push_str(s); } @@ -506,7 +507,17 @@ enum ArgRef<'a> { // If there's a lot of unused arguments, // let's check if this format arguments looks like another syntax (printf / shell). let detect_foreign_fmt = unused.len() > args.explicit_args().len() / 2; - report_missing_placeholders(ecx, unused, detect_foreign_fmt, str_style, fmt_str, fmt_span); + report_missing_placeholders( + ecx, + unused, + &used, + &args, + &pieces, + detect_foreign_fmt, + str_style, + fmt_str, + fmt_span, + ); } // Only check for unused named argument names if there are no other errors to avoid causing @@ -573,6 +584,9 @@ fn invalid_placeholder_type_error( fn report_missing_placeholders( ecx: &mut ExtCtxt<'_>, unused: Vec<(Span, bool)>, + used: &[bool], + args: &FormatArguments, + pieces: &[parse::Piece<'_>], detect_foreign_fmt: bool, str_style: Option, fmt_str: &str, @@ -591,6 +605,68 @@ fn report_missing_placeholders( }) }; + let placeholders = pieces + .iter() + .filter_map(|piece| { + if let parse::Piece::NextArgument(argument) = piece && let ArgumentNamed(binding) = argument.position { + let span = fmt_span.from_inner(InnerSpan::new(argument.position_span.start, argument.position_span.end)); + Some((span, binding)) + } else { None } + }) + .collect::>(); + + let mut args_spans = vec![]; + let mut fmt_spans = FxIndexSet::default(); + + for (i, unnamed_arg) in args.unnamed_args().iter().enumerate().rev() { + let Some(ty) = unnamed_arg.expr.to_ty() else { continue }; + let Some(argument_binding) = ty.kind.is_simple_path() else { continue }; + let argument_binding = argument_binding.as_str(); + + if used[i] { + continue; + } + + let matching_placeholders = placeholders + .iter() + .filter(|(_, inline_binding)| argument_binding == *inline_binding) + .collect::>(); + + if !matching_placeholders.is_empty() { + args_spans.push(unnamed_arg.expr.span); + for placeholder in &matching_placeholders { + fmt_spans.insert(*placeholder); + } + } + } + + if !args_spans.is_empty() { + let mut multispan = MultiSpan::from(args_spans.clone()); + + 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 (span, binding) in fmt_spans { + multispan.push_span_label( + *span, + format!("this formatting specifier is referencing the `{binding}` binding"), + ); + } + + for span in &args_spans { + multispan.push_span_label(*span, "this can be removed"); + } + + diag.span_help(multispan, msg); + diag.emit(); + return; + } + // Used to ensure we only report translations for *one* kind of foreign format. let mut found_foreign = false; diff --git a/tests/ui/did_you_mean/issue-105225.rs b/tests/ui/did_you_mean/issue-105225.rs new file mode 100644 index 00000000000..5d288c7eb24 --- /dev/null +++ b/tests/ui/did_you_mean/issue-105225.rs @@ -0,0 +1,19 @@ +fn main() { + let x = 0; + println!("{x}", x); + //~^ ERROR: argument never used + + println!("{x} {}", x, x); + //~^ ERROR: argument never used + + println!("{} {x}", x, x); + //~^ ERROR: argument never used + + let y = 0; + println!("{x} {y}", x, y); + //~^ ERROR: multiple unused formatting arguments + + let y = 0; + println!("{} {} {x} {y} {}", x, x, x, y, y); + //~^ ERROR: multiple unused formatting arguments +} diff --git a/tests/ui/did_you_mean/issue-105225.stderr b/tests/ui/did_you_mean/issue-105225.stderr new file mode 100644 index 00000000000..c067d396367 --- /dev/null +++ b/tests/ui/did_you_mean/issue-105225.stderr @@ -0,0 +1,81 @@ +error: argument never used + --> $DIR/issue-105225.rs:3:21 + | +LL | println!("{x}", x); + | ^ argument never used + | +help: the formatting string already captures the binding directly, it doesn't need to be included in the argument list + --> $DIR/issue-105225.rs:3:21 + | +LL | println!("{x}", x); + | - ^ this can be removed + | | + | this formatting specifier is referencing the `x` binding + +error: argument never used + --> $DIR/issue-105225.rs:6:27 + | +LL | println!("{x} {}", x, x); + | ^ argument never used + | +help: the formatting string already captures the binding directly, it doesn't need to be included in the argument list + --> $DIR/issue-105225.rs:6:27 + | +LL | println!("{x} {}", x, x); + | - ^ this can be removed + | | + | this formatting specifier is referencing the `x` binding + +error: argument never used + --> $DIR/issue-105225.rs:9:27 + | +LL | println!("{} {x}", x, x); + | ^ argument never used + | +help: the formatting string already captures the binding directly, it doesn't need to be included in the argument list + --> $DIR/issue-105225.rs:9:27 + | +LL | println!("{} {x}", x, x); + | - ^ this can be removed + | | + | this formatting specifier is referencing the `x` binding + +error: multiple unused formatting arguments + --> $DIR/issue-105225.rs:13:25 + | +LL | println!("{x} {y}", x, y); + | --------- ^ ^ argument never used + | | | + | | argument never used + | multiple missing formatting specifiers + | +help: the formatting strings already captures the bindings directly, they don't need to be included in the argument list + --> $DIR/issue-105225.rs:13:25 + | +LL | println!("{x} {y}", x, y); + | - - ^ ^ this can be removed + | | | | + | | | this can be removed + | | this formatting specifier is referencing the `y` binding + | this formatting specifier is referencing the `x` binding + +error: multiple unused formatting arguments + --> $DIR/issue-105225.rs:17:43 + | +LL | println!("{} {} {x} {y} {}", x, x, x, y, y); + | ------------------ ^ ^ argument never used + | | | + | | argument never used + | multiple missing formatting specifiers + | +help: the formatting string already captures the binding directly, it doesn't need to be included in the argument list + --> $DIR/issue-105225.rs:17:43 + | +LL | println!("{} {} {x} {y} {}", x, x, x, y, y); + | - ^ ^ this can be removed + | | | + | | this can be removed + | this formatting specifier is referencing the `y` binding + +error: aborting due to 5 previous errors + From 4cc2d0bd6569bc684892905af22c7492ecd1f91a Mon Sep 17 00:00:00 2001 From: francorbacho Date: Mon, 25 Sep 2023 16:50:17 +0200 Subject: [PATCH 02/12] Separate report_redundant_placeholders() into its own function --- compiler/rustc_builtin_macros/src/format.rs | 114 +++++++++++--------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index fd286391437..6db551aef35 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -8,11 +8,11 @@ FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, }; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::{Applicability, MultiSpan, PResult, SingleLabelManySpans}; +use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan, PResult, SingleLabelManySpans}; use rustc_expand::base::{self, *}; use rustc_parse_format as parse; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{BytePos, InnerSpan, Span}; +use rustc_span::{BytePos, ErrorGuaranteed, InnerSpan, Span}; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId}; @@ -615,56 +615,8 @@ fn report_missing_placeholders( }) .collect::>(); - let mut args_spans = vec![]; - let mut fmt_spans = FxIndexSet::default(); - - for (i, unnamed_arg) in args.unnamed_args().iter().enumerate().rev() { - let Some(ty) = unnamed_arg.expr.to_ty() else { continue }; - let Some(argument_binding) = ty.kind.is_simple_path() else { continue }; - let argument_binding = argument_binding.as_str(); - - if used[i] { - continue; - } - - let matching_placeholders = placeholders - .iter() - .filter(|(_, inline_binding)| argument_binding == *inline_binding) - .collect::>(); - - if !matching_placeholders.is_empty() { - args_spans.push(unnamed_arg.expr.span); - for placeholder in &matching_placeholders { - fmt_spans.insert(*placeholder); - } - } - } - - if !args_spans.is_empty() { - let mut multispan = MultiSpan::from(args_spans.clone()); - - 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 (span, binding) in fmt_spans { - multispan.push_span_label( - *span, - format!("this formatting specifier is referencing the `{binding}` binding"), - ); - } - - for span in &args_spans { - multispan.push_span_label(*span, "this can be removed"); - } - - diag.span_help(multispan, msg); - diag.emit(); - return; + if !placeholders.is_empty() { + report_redundant_placeholders(&mut diag, &args, used, placeholders); } // Used to ensure we only report translations for *one* kind of foreign format. @@ -754,6 +706,64 @@ macro_rules! check_foreign { diag.emit(); } +fn report_redundant_placeholders( + diag: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + args: &FormatArguments, + used: &[bool], + placeholders: Vec<(Span, &str)>, +) { + let mut args_spans = vec![]; + let mut fmt_spans = FxIndexSet::default(); + + for (i, unnamed_arg) in args.unnamed_args().iter().enumerate().rev() { + let Some(ty) = unnamed_arg.expr.to_ty() else { continue }; + let Some(argument_binding) = ty.kind.is_simple_path() else { continue }; + let argument_binding = argument_binding.as_str(); + + if used[i] { + continue; + } + + let matching_placeholders = placeholders + .iter() + .filter(|(_, inline_binding)| argument_binding == *inline_binding) + .collect::>(); + + if !matching_placeholders.is_empty() { + args_spans.push(unnamed_arg.expr.span); + for placeholder in &matching_placeholders { + fmt_spans.insert(*placeholder); + } + } + } + + if !args_spans.is_empty() { + let mut multispan = MultiSpan::from(args_spans.clone()); + + 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 (span, binding) in fmt_spans { + multispan.push_span_label( + *span, + format!("this formatting specifier is referencing the `{binding}` binding"), + ); + } + + for span in &args_spans { + multispan.push_span_label(*span, "this can be removed"); + } + + diag.span_help(multispan, msg); + diag.emit(); + } +} + /// Handle invalid references to positional arguments. Output different /// errors for the case where all arguments are positional and for when /// there are named arguments or numbered positional arguments in the From 04fc051a345c861efe8b2053d62cc1313824731a Mon Sep 17 00:00:00 2001 From: francorbacho Date: Wed, 4 Oct 2023 15:47:53 +0200 Subject: [PATCH 03/12] Use diagnostic impls and add suggestions in redundant format!() args --- compiler/rustc_builtin_macros/messages.ftl | 11 +++ compiler/rustc_builtin_macros/src/errors.rs | 21 +++++ compiler/rustc_builtin_macros/src/format.rs | 58 ++++++++------ tests/ui/did_you_mean/issue-105225.rs | 10 +-- tests/ui/did_you_mean/issue-105225.stderr | 89 +++++++++------------ 5 files changed, 110 insertions(+), 79 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 207ae8ad844..ce3d07c59de 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -139,6 +139,17 @@ builtin_macros_format_positional_after_named = positional arguments cannot follo builtin_macros_format_remove_raw_ident = remove the `r#` +builtin_macros_format_redundant_args = redundant argument + .help = {$n -> + [one] the formatting string already captures the binding directly, it doesn't need to be included in the argument list + *[more] the formatting strings already captures the bindings directly, they don't need to be included in the argument list + } + .note = {$n -> + [one] the formatting specifier is referencing the binding already + *[more] the formatting specifiers are referencing the bindings already + } + .suggestion = this can be removed + builtin_macros_format_requires_string = requires at least a format string argument builtin_macros_format_string_invalid = invalid format string: {$desc} 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 @@ -646,6 +646,27 @@ pub(crate) struct FormatPositionalMismatch { pub(crate) highlight: SingleLabelManySpans, } +#[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, +} + #[derive(Diagnostic)] #[diag(builtin_macros_test_case_non_item)] pub(crate) struct TestCaseNonItem { 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 @@ 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::>(); 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 @@ macro_rules! check_foreign { } 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::>(); 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(); } } diff --git a/tests/ui/did_you_mean/issue-105225.rs b/tests/ui/did_you_mean/issue-105225.rs index 5d288c7eb24..49070b52dd0 100644 --- a/tests/ui/did_you_mean/issue-105225.rs +++ b/tests/ui/did_you_mean/issue-105225.rs @@ -1,19 +1,19 @@ fn main() { let x = 0; println!("{x}", x); - //~^ ERROR: argument never used + //~^ ERROR: redundant argument println!("{x} {}", x, x); - //~^ ERROR: argument never used + //~^ ERROR: redundant argument println!("{} {x}", x, x); - //~^ ERROR: argument never used + //~^ ERROR: redundant argument let y = 0; println!("{x} {y}", x, y); - //~^ ERROR: multiple unused formatting arguments + //~^ ERROR: redundant argument let y = 0; println!("{} {} {x} {y} {}", x, x, x, y, y); - //~^ ERROR: multiple unused formatting arguments + //~^ ERROR: redundant argument } diff --git a/tests/ui/did_you_mean/issue-105225.stderr b/tests/ui/did_you_mean/issue-105225.stderr index c067d396367..8c366f31311 100644 --- a/tests/ui/did_you_mean/issue-105225.stderr +++ b/tests/ui/did_you_mean/issue-105225.stderr @@ -1,81 +1,72 @@ -error: argument never used - --> $DIR/issue-105225.rs:3:21 +error: redundant argument + --> $DIR/issue-105225.rs:3:14 | LL | println!("{x}", x); - | ^ argument never used + | ^^^^^ - help: this can be removed | -help: the formatting string already captures the binding directly, it doesn't need to be included in the argument list - --> $DIR/issue-105225.rs:3:21 +note: the formatting specifier is referencing the binding already + --> $DIR/issue-105225.rs:3:16 | LL | println!("{x}", x); - | - ^ this can be removed - | | - | this formatting specifier is referencing the `x` binding + | ^ -error: argument never used - --> $DIR/issue-105225.rs:6:27 +error: redundant argument + --> $DIR/issue-105225.rs:6:14 | LL | println!("{x} {}", x, x); - | ^ argument never used + | ^^^^^^^^ - help: this can be removed | -help: the formatting string already captures the binding directly, it doesn't need to be included in the argument list - --> $DIR/issue-105225.rs:6:27 +note: the formatting specifier is referencing the binding already + --> $DIR/issue-105225.rs:6:16 | LL | println!("{x} {}", x, x); - | - ^ this can be removed - | | - | this formatting specifier is referencing the `x` binding + | ^ -error: argument never used - --> $DIR/issue-105225.rs:9:27 +error: redundant argument + --> $DIR/issue-105225.rs:9:14 | LL | println!("{} {x}", x, x); - | ^ argument never used + | ^^^^^^^^ - help: this can be removed | -help: the formatting string already captures the binding directly, it doesn't need to be included in the argument list - --> $DIR/issue-105225.rs:9:27 +note: the formatting specifier is referencing the binding already + --> $DIR/issue-105225.rs:9:19 | LL | println!("{} {x}", x, x); - | - ^ this can be removed - | | - | this formatting specifier is referencing the `x` binding + | ^ -error: multiple unused formatting arguments - --> $DIR/issue-105225.rs:13:25 +error: redundant argument + --> $DIR/issue-105225.rs:13:14 | LL | println!("{x} {y}", x, y); - | --------- ^ ^ argument never used - | | | - | | argument never used - | multiple missing formatting specifiers + | ^^^^^^^^^ | -help: the formatting strings already captures the bindings directly, they don't need to be included in the argument list - --> $DIR/issue-105225.rs:13:25 +note: the formatting specifiers are referencing the bindings already + --> $DIR/issue-105225.rs:13:16 | LL | println!("{x} {y}", x, y); - | - - ^ ^ this can be removed - | | | | - | | | this can be removed - | | this formatting specifier is referencing the `y` binding - | this formatting specifier is referencing the `x` binding + | ^ ^ +help: this can be removed + | +LL - println!("{x} {y}", x, y); +LL + println!("{x} {y}", ); + | -error: multiple unused formatting arguments - --> $DIR/issue-105225.rs:17:43 +error: redundant argument + --> $DIR/issue-105225.rs:17:14 | LL | println!("{} {} {x} {y} {}", x, x, x, y, y); - | ------------------ ^ ^ argument never used - | | | - | | argument never used - | multiple missing formatting specifiers + | ^^^^^^^^^^^^^^^^^^ | -help: the formatting string already captures the binding directly, it doesn't need to be included in the argument list - --> $DIR/issue-105225.rs:17:43 +note: the formatting specifiers are referencing the bindings already + --> $DIR/issue-105225.rs:17:26 | LL | println!("{} {} {x} {y} {}", x, x, x, y, y); - | - ^ ^ this can be removed - | | | - | | this can be removed - | this formatting specifier is referencing the `y` binding + | ^ +help: this can be removed + | +LL - println!("{} {} {x} {y} {}", x, x, x, y, y); +LL + println!("{} {} {x} {y} {}", x, x, x, ); + | error: aborting due to 5 previous errors From 9c921f843c78a98b8b0eaa40f2b20b60543ebf53 Mon Sep 17 00:00:00 2001 From: francorbacho Date: Wed, 4 Oct 2023 17:35:39 +0200 Subject: [PATCH 04/12] Rename report_redundant_placeholders() to report_redundant_format_arguments() --- compiler/rustc_builtin_macros/src/format.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 32a90b307ef..8cdab65b6b3 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -616,7 +616,7 @@ fn report_missing_placeholders( .collect::>(); if !placeholders.is_empty() { - report_redundant_placeholders(ecx, fmt_span, &args, used, placeholders); + report_redundant_format_arguments(ecx, fmt_span, &args, used, placeholders); diag.cancel(); return; } @@ -708,7 +708,7 @@ macro_rules! check_foreign { diag.emit(); } -fn report_redundant_placeholders( +fn report_redundant_format_arguments( ecx: &mut ExtCtxt<'_>, fmt_span: Span, args: &FormatArguments, From e1c18f4d9de902ad615035bfed4e173d72f7ff09 Mon Sep 17 00:00:00 2001 From: francorbacho Date: Wed, 4 Oct 2023 17:38:46 +0200 Subject: [PATCH 05/12] Document report_redundant_format_arguments() --- compiler/rustc_builtin_macros/src/format.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 8cdab65b6b3..2d91de105cd 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -708,6 +708,8 @@ macro_rules! check_foreign { diag.emit(); } +/// This function detects and reports unused format!() arguments that are +/// redundant due to implicit captures (e.g. `format!("{x}", x)`). fn report_redundant_format_arguments( ecx: &mut ExtCtxt<'_>, fmt_span: Span, From dc75c99226f948c3459f9970dbd6fe0b5d2bbef2 Mon Sep 17 00:00:00 2001 From: francorbacho Date: Wed, 4 Oct 2023 17:46:48 +0200 Subject: [PATCH 06/12] Remove unused variable --- compiler/rustc_builtin_macros/src/format.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 2d91de105cd..cdd5e7225bb 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -720,7 +720,6 @@ fn report_redundant_format_arguments( let mut fmt_arg_indices = vec![]; let mut args_spans = vec![]; 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 }; @@ -734,17 +733,17 @@ fn report_redundant_format_arguments( let matching_placeholders = placeholders .iter() .filter(|(_, inline_binding)| argument_binding == *inline_binding) + .map(|(span, _)| span) .collect::>(); if !matching_placeholders.is_empty() { fmt_arg_indices.push(i); args_spans.push(unnamed_arg.expr.span); - for (span, binding) in &matching_placeholders { - if fmt_spans.contains(span) { + for span in &matching_placeholders { + if fmt_spans.contains(*span) { continue; } - fmt_spans.push(*span); - bindings.push(binding); + fmt_spans.push(**span); } } } From 4a7a98cf3058f68f94c3cac1e6dfaa2f3282680c Mon Sep 17 00:00:00 2001 From: francorbacho Date: Thu, 5 Oct 2023 14:23:02 +0200 Subject: [PATCH 07/12] Fix diagnostics being cancelled even with unused arguments --- compiler/rustc_builtin_macros/src/format.rs | 28 +++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index cdd5e7225bb..d7fc48026cb 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -8,7 +8,9 @@ FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, }; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, MultiSpan, PResult, SingleLabelManySpans}; +use rustc_errors::{ + Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult, SingleLabelManySpans, +}; use rustc_expand::base::{self, *}; use rustc_parse_format as parse; use rustc_span::symbol::{Ident, Symbol}; @@ -616,9 +618,13 @@ fn report_missing_placeholders( .collect::>(); if !placeholders.is_empty() { - report_redundant_format_arguments(ecx, fmt_span, &args, used, placeholders); - diag.cancel(); - return; + if let Some(mut new_diag) = + report_redundant_format_arguments(ecx, fmt_span, &args, used, placeholders) + { + diag.cancel(); + new_diag.emit(); + return; + } } // Used to ensure we only report translations for *one* kind of foreign format. @@ -710,13 +716,13 @@ macro_rules! check_foreign { /// This function detects and reports unused format!() arguments that are /// redundant due to implicit captures (e.g. `format!("{x}", x)`). -fn report_redundant_format_arguments( - ecx: &mut ExtCtxt<'_>, +fn report_redundant_format_arguments<'a>( + ecx: &mut ExtCtxt<'a>, fmt_span: Span, args: &FormatArguments, used: &[bool], placeholders: Vec<(Span, &str)>, -) { +) -> Option> { let mut fmt_arg_indices = vec![]; let mut args_spans = vec![]; let mut fmt_spans = vec![]; @@ -762,15 +768,15 @@ fn report_redundant_format_arguments( suggestion_spans.push(span); } - let mut diag = ecx.create_err(errors::FormatRedundantArgs { + return Some(ecx.create_err(errors::FormatRedundantArgs { fmt_span, note: multispan, n: args_spans.len(), sugg: errors::FormatRedundantArgsSugg { spans: suggestion_spans }, - }); - - diag.emit(); + })); } + + None } /// Handle invalid references to positional arguments. Output different From fcdd5c0b2d754c513241d62c5692be2197458b08 Mon Sep 17 00:00:00 2001 From: francorbacho Date: Thu, 5 Oct 2023 14:32:56 +0200 Subject: [PATCH 08/12] Plurals in format redundant arguments suggestion --- compiler/rustc_builtin_macros/messages.ftl | 5 ++++- tests/ui/did_you_mean/issue-105225.stderr | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index ce3d07c59de..8f158fb17da 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -139,7 +139,10 @@ builtin_macros_format_positional_after_named = positional arguments cannot follo builtin_macros_format_remove_raw_ident = remove the `r#` -builtin_macros_format_redundant_args = redundant argument +builtin_macros_format_redundant_args = redundant {$n -> + [one] argument + *[more] arguments + } .help = {$n -> [one] the formatting string already captures the binding directly, it doesn't need to be included in the argument list *[more] the formatting strings already captures the bindings directly, they don't need to be included in the argument list diff --git a/tests/ui/did_you_mean/issue-105225.stderr b/tests/ui/did_you_mean/issue-105225.stderr index 8c366f31311..dc18e7268b4 100644 --- a/tests/ui/did_you_mean/issue-105225.stderr +++ b/tests/ui/did_you_mean/issue-105225.stderr @@ -34,7 +34,7 @@ note: the formatting specifier is referencing the binding already LL | println!("{} {x}", x, x); | ^ -error: redundant argument +error: redundant arguments --> $DIR/issue-105225.rs:13:14 | LL | println!("{x} {y}", x, y); @@ -51,7 +51,7 @@ LL - println!("{x} {y}", x, y); LL + println!("{x} {y}", ); | -error: redundant argument +error: redundant arguments --> $DIR/issue-105225.rs:17:14 | LL | println!("{} {} {x} {y} {}", x, x, x, y, y); From 905bace904ded95ef6996684ede37486313b640f Mon Sep 17 00:00:00 2001 From: francorbacho Date: Thu, 5 Oct 2023 14:36:50 +0200 Subject: [PATCH 09/12] Highlight redundant arguments instead of the whole format string --- compiler/rustc_builtin_macros/src/errors.rs | 2 +- compiler/rustc_builtin_macros/src/format.rs | 7 +++--- tests/ui/did_you_mean/issue-105225.rs | 8 +++--- tests/ui/did_you_mean/issue-105225.stderr | 28 ++++++++++----------- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index c2097f0bc47..7a8ec5cfdf3 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -650,7 +650,7 @@ pub(crate) struct FormatPositionalMismatch { #[diag(builtin_macros_format_redundant_args)] pub(crate) struct FormatRedundantArgs { #[primary_span] - pub(crate) fmt_span: Span, + pub(crate) span: MultiSpan, pub(crate) n: usize, #[note] diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index d7fc48026cb..e08fded01fb 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -619,7 +619,7 @@ fn report_missing_placeholders( if !placeholders.is_empty() { if let Some(mut new_diag) = - report_redundant_format_arguments(ecx, fmt_span, &args, used, placeholders) + report_redundant_format_arguments(ecx, &args, used, placeholders) { diag.cancel(); new_diag.emit(); @@ -718,7 +718,6 @@ macro_rules! check_foreign { /// redundant due to implicit captures (e.g. `format!("{x}", x)`). fn report_redundant_format_arguments<'a>( ecx: &mut ExtCtxt<'a>, - fmt_span: Span, args: &FormatArguments, used: &[bool], placeholders: Vec<(Span, &str)>, @@ -769,9 +768,9 @@ fn report_redundant_format_arguments<'a>( } return Some(ecx.create_err(errors::FormatRedundantArgs { - fmt_span, - note: multispan, n: args_spans.len(), + span: MultiSpan::from(args_spans), + note: multispan, sugg: errors::FormatRedundantArgsSugg { spans: suggestion_spans }, })); } diff --git a/tests/ui/did_you_mean/issue-105225.rs b/tests/ui/did_you_mean/issue-105225.rs index 49070b52dd0..5c0d17f5839 100644 --- a/tests/ui/did_you_mean/issue-105225.rs +++ b/tests/ui/did_you_mean/issue-105225.rs @@ -1,5 +1,7 @@ fn main() { let x = 0; + let y = 0; + println!("{x}", x); //~^ ERROR: redundant argument @@ -9,11 +11,9 @@ fn main() { println!("{} {x}", x, x); //~^ ERROR: redundant argument - let y = 0; println!("{x} {y}", x, y); - //~^ ERROR: redundant argument + //~^ ERROR: redundant arguments - let y = 0; println!("{} {} {x} {y} {}", x, x, x, y, y); - //~^ ERROR: redundant argument + //~^ ERROR: redundant arguments } diff --git a/tests/ui/did_you_mean/issue-105225.stderr b/tests/ui/did_you_mean/issue-105225.stderr index dc18e7268b4..b9b04a0fe2e 100644 --- a/tests/ui/did_you_mean/issue-105225.stderr +++ b/tests/ui/did_you_mean/issue-105225.stderr @@ -1,47 +1,47 @@ error: redundant argument - --> $DIR/issue-105225.rs:3:14 + --> $DIR/issue-105225.rs:5:21 | LL | println!("{x}", x); - | ^^^^^ - help: this can be removed + | ^ help: this can be removed | note: the formatting specifier is referencing the binding already - --> $DIR/issue-105225.rs:3:16 + --> $DIR/issue-105225.rs:5:16 | LL | println!("{x}", x); | ^ error: redundant argument - --> $DIR/issue-105225.rs:6:14 + --> $DIR/issue-105225.rs:8:27 | LL | println!("{x} {}", x, x); - | ^^^^^^^^ - help: this can be removed + | ^ help: this can be removed | note: the formatting specifier is referencing the binding already - --> $DIR/issue-105225.rs:6:16 + --> $DIR/issue-105225.rs:8:16 | LL | println!("{x} {}", x, x); | ^ error: redundant argument - --> $DIR/issue-105225.rs:9:14 + --> $DIR/issue-105225.rs:11:27 | LL | println!("{} {x}", x, x); - | ^^^^^^^^ - help: this can be removed + | ^ help: this can be removed | note: the formatting specifier is referencing the binding already - --> $DIR/issue-105225.rs:9:19 + --> $DIR/issue-105225.rs:11:19 | LL | println!("{} {x}", x, x); | ^ error: redundant arguments - --> $DIR/issue-105225.rs:13:14 + --> $DIR/issue-105225.rs:14:25 | LL | println!("{x} {y}", x, y); - | ^^^^^^^^^ + | ^ ^ | note: the formatting specifiers are referencing the bindings already - --> $DIR/issue-105225.rs:13:16 + --> $DIR/issue-105225.rs:14:16 | LL | println!("{x} {y}", x, y); | ^ ^ @@ -52,10 +52,10 @@ LL + println!("{x} {y}", ); | error: redundant arguments - --> $DIR/issue-105225.rs:17:14 + --> $DIR/issue-105225.rs:17:43 | LL | println!("{} {} {x} {y} {}", x, x, x, y, y); - | ^^^^^^^^^^^^^^^^^^ + | ^ ^ | note: the formatting specifiers are referencing the bindings already --> $DIR/issue-105225.rs:17:26 From 38b01828328014708feb48f9cdc065460e651098 Mon Sep 17 00:00:00 2001 From: francorbacho Date: Thu, 5 Oct 2023 14:59:15 +0200 Subject: [PATCH 10/12] Add suggestion test --- tests/ui/did_you_mean/issue-105225.fixed | 21 +++++++++++++++++++++ tests/ui/did_you_mean/issue-105225.rs | 2 ++ tests/ui/did_you_mean/issue-105225.stderr | 20 ++++++++++---------- 3 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 tests/ui/did_you_mean/issue-105225.fixed diff --git a/tests/ui/did_you_mean/issue-105225.fixed b/tests/ui/did_you_mean/issue-105225.fixed new file mode 100644 index 00000000000..30c99221912 --- /dev/null +++ b/tests/ui/did_you_mean/issue-105225.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +fn main() { + let x = 0; + let y = 0; + + println!("{x}", ); + //~^ ERROR: redundant argument + + println!("{x} {}", x, ); + //~^ ERROR: redundant argument + + println!("{} {x}", x, ); + //~^ ERROR: redundant argument + + println!("{x} {y}", ); + //~^ ERROR: redundant arguments + + println!("{} {} {x} {y} {}", x, x, x, ); + //~^ ERROR: redundant arguments +} diff --git a/tests/ui/did_you_mean/issue-105225.rs b/tests/ui/did_you_mean/issue-105225.rs index 5c0d17f5839..a69c90a6490 100644 --- a/tests/ui/did_you_mean/issue-105225.rs +++ b/tests/ui/did_you_mean/issue-105225.rs @@ -1,3 +1,5 @@ +// run-rustfix + fn main() { let x = 0; let y = 0; diff --git a/tests/ui/did_you_mean/issue-105225.stderr b/tests/ui/did_you_mean/issue-105225.stderr index b9b04a0fe2e..5fb46222bee 100644 --- a/tests/ui/did_you_mean/issue-105225.stderr +++ b/tests/ui/did_you_mean/issue-105225.stderr @@ -1,47 +1,47 @@ error: redundant argument - --> $DIR/issue-105225.rs:5:21 + --> $DIR/issue-105225.rs:7:21 | LL | println!("{x}", x); | ^ help: this can be removed | note: the formatting specifier is referencing the binding already - --> $DIR/issue-105225.rs:5:16 + --> $DIR/issue-105225.rs:7:16 | LL | println!("{x}", x); | ^ error: redundant argument - --> $DIR/issue-105225.rs:8:27 + --> $DIR/issue-105225.rs:10:27 | LL | println!("{x} {}", x, x); | ^ help: this can be removed | note: the formatting specifier is referencing the binding already - --> $DIR/issue-105225.rs:8:16 + --> $DIR/issue-105225.rs:10:16 | LL | println!("{x} {}", x, x); | ^ error: redundant argument - --> $DIR/issue-105225.rs:11:27 + --> $DIR/issue-105225.rs:13:27 | LL | println!("{} {x}", x, x); | ^ help: this can be removed | note: the formatting specifier is referencing the binding already - --> $DIR/issue-105225.rs:11:19 + --> $DIR/issue-105225.rs:13:19 | LL | println!("{} {x}", x, x); | ^ error: redundant arguments - --> $DIR/issue-105225.rs:14:25 + --> $DIR/issue-105225.rs:16:25 | LL | println!("{x} {y}", x, y); | ^ ^ | note: the formatting specifiers are referencing the bindings already - --> $DIR/issue-105225.rs:14:16 + --> $DIR/issue-105225.rs:16:16 | LL | println!("{x} {y}", x, y); | ^ ^ @@ -52,13 +52,13 @@ LL + println!("{x} {y}", ); | error: redundant arguments - --> $DIR/issue-105225.rs:17:43 + --> $DIR/issue-105225.rs:19:43 | LL | println!("{} {} {x} {y} {}", x, x, x, y, y); | ^ ^ | note: the formatting specifiers are referencing the bindings already - --> $DIR/issue-105225.rs:17:26 + --> $DIR/issue-105225.rs:19:26 | LL | println!("{} {} {x} {y} {}", x, x, x, y, y); | ^ From c8ee7db6ea272b42e90602007807be6c2191f96b Mon Sep 17 00:00:00 2001 From: francorbacho Date: Thu, 5 Oct 2023 15:46:34 +0200 Subject: [PATCH 11/12] Only give autofix suggestion when no named args are present --- compiler/rustc_builtin_macros/src/errors.rs | 2 +- compiler/rustc_builtin_macros/src/format.rs | 8 ++++++- .../did_you_mean/issue-105225-named-args.rs | 10 +++++++++ .../issue-105225-named-args.stderr | 22 +++++++++++++++++++ tests/ui/did_you_mean/issue-105225.fixed | 4 ++-- tests/ui/did_you_mean/issue-105225.rs | 4 ++-- 6 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 tests/ui/did_you_mean/issue-105225-named-args.rs create mode 100644 tests/ui/did_you_mean/issue-105225-named-args.stderr diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 7a8ec5cfdf3..fde4270334b 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -657,7 +657,7 @@ pub(crate) struct FormatRedundantArgs { pub(crate) note: MultiSpan, #[subdiagnostic] - pub(crate) sugg: FormatRedundantArgsSugg, + pub(crate) sugg: Option, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index e08fded01fb..7c78be17653 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -767,11 +767,17 @@ fn report_redundant_format_arguments<'a>( suggestion_spans.push(span); } + let sugg = if args.named_args().len() == 0 { + Some(errors::FormatRedundantArgsSugg { spans: suggestion_spans }) + } else { + None + }; + return Some(ecx.create_err(errors::FormatRedundantArgs { n: args_spans.len(), span: MultiSpan::from(args_spans), note: multispan, - sugg: errors::FormatRedundantArgsSugg { spans: suggestion_spans }, + sugg, })); } diff --git a/tests/ui/did_you_mean/issue-105225-named-args.rs b/tests/ui/did_you_mean/issue-105225-named-args.rs new file mode 100644 index 00000000000..38e81776576 --- /dev/null +++ b/tests/ui/did_you_mean/issue-105225-named-args.rs @@ -0,0 +1,10 @@ +fn main() { + let x = "x"; + let y = "y"; + + println!("{x}", x, x = y); + //~^ ERROR: redundant argument + + println!("{x}", x = y, x = y); + //~^ ERROR: duplicate argument named `x` +} diff --git a/tests/ui/did_you_mean/issue-105225-named-args.stderr b/tests/ui/did_you_mean/issue-105225-named-args.stderr new file mode 100644 index 00000000000..72204102ef6 --- /dev/null +++ b/tests/ui/did_you_mean/issue-105225-named-args.stderr @@ -0,0 +1,22 @@ +error: redundant argument + --> $DIR/issue-105225-named-args.rs:5:21 + | +LL | println!("{x}", x, x = y); + | ^ + | +note: the formatting specifier is referencing the binding already + --> $DIR/issue-105225-named-args.rs:5:16 + | +LL | println!("{x}", x, x = y); + | ^ + +error: duplicate argument named `x` + --> $DIR/issue-105225-named-args.rs:8:28 + | +LL | println!("{x}", x = y, x = y); + | - ^ duplicate argument + | | + | previously here + +error: aborting due to 2 previous errors + diff --git a/tests/ui/did_you_mean/issue-105225.fixed b/tests/ui/did_you_mean/issue-105225.fixed index 30c99221912..f756be615a1 100644 --- a/tests/ui/did_you_mean/issue-105225.fixed +++ b/tests/ui/did_you_mean/issue-105225.fixed @@ -1,8 +1,8 @@ // run-rustfix fn main() { - let x = 0; - let y = 0; + let x = "x"; + let y = "y"; println!("{x}", ); //~^ ERROR: redundant argument diff --git a/tests/ui/did_you_mean/issue-105225.rs b/tests/ui/did_you_mean/issue-105225.rs index a69c90a6490..91cdf0eb28f 100644 --- a/tests/ui/did_you_mean/issue-105225.rs +++ b/tests/ui/did_you_mean/issue-105225.rs @@ -1,8 +1,8 @@ // run-rustfix fn main() { - let x = 0; - let y = 0; + let x = "x"; + let y = "y"; println!("{x}", x); //~^ ERROR: redundant argument From cbc6b65d9a4a404392a928dfc200a7616cfc2c98 Mon Sep 17 00:00:00 2001 From: francorbacho Date: Thu, 5 Oct 2023 16:16:31 +0200 Subject: [PATCH 12/12] Keep fluent slugs in alphabetical order --- compiler/rustc_builtin_macros/messages.ftl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 8f158fb17da..dda466b026d 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -137,8 +137,6 @@ builtin_macros_format_positional_after_named = positional arguments cannot follo .label = positional arguments must be before named arguments .named_args = named argument -builtin_macros_format_remove_raw_ident = remove the `r#` - builtin_macros_format_redundant_args = redundant {$n -> [one] argument *[more] arguments @@ -153,6 +151,8 @@ builtin_macros_format_redundant_args = redundant {$n -> } .suggestion = this can be removed +builtin_macros_format_remove_raw_ident = remove the `r#` + builtin_macros_format_requires_string = requires at least a format string argument builtin_macros_format_string_invalid = invalid format string: {$desc}