diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 7a31d2a223917..4cac7cb93f581 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -197,6 +197,8 @@ builtin_macros_format_redundant_args = redundant {$n -> builtin_macros_format_remove_raw_ident = remove the `r#` +builtin_macros_format_reorder_format_parameter = did you mean `{$replacement}`? + 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 1abdfdb9c65c9..6213bd802c751 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -618,6 +618,17 @@ pub(crate) enum InvalidFormatStringSuggestion { #[primary_span] span: Span, }, + #[suggestion( + builtin_macros_format_reorder_format_parameter, + code = "{replacement}", + style = "verbose", + applicability = "machine-applicable" + )] + ReorderFormatParameter { + #[primary_span] + span: Span, + replacement: String, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 0112499c50949..5202fe26c401e 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -321,6 +321,13 @@ fn make_format_args( e.sugg_ = Some(errors::InvalidFormatStringSuggestion::RemoveRawIdent { span }) } } + parse::Suggestion::ReorderFormatParameter(span, replacement) => { + let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); + e.sugg_ = Some(errors::InvalidFormatStringSuggestion::ReorderFormatParameter { + span, + replacement, + }); + } } let guar = ecx.dcx().emit_err(e); return ExpandResult::Ready(Err(guar)); diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 1716f417969e1..9eb335cb34cc2 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -221,6 +221,11 @@ pub enum Suggestion { /// Remove `r#` from identifier: /// `format!("{r#foo}")` -> `format!("{foo}")` RemoveRawIdent(InnerSpan), + /// Reorder format parameter: + /// `format!("{foo:?#}")` -> `format!("{foo:#?}")` + /// `format!("{foo:?x}")` -> `format!("{foo:x?}")` + /// `format!("{foo:?X}")` -> `format!("{foo:X?}")` + ReorderFormatParameter(InnerSpan, string::String), } /// The parser structure for interpreting the input format string. This is @@ -731,6 +736,12 @@ impl<'a> Parser<'a> { } } else if self.consume('?') { spec.ty = "?"; + if let Some(&(_, maybe)) = self.cur.peek() { + match maybe { + '#' | 'x' | 'X' => self.suggest_format_parameter(maybe), + _ => (), + } + } } else { spec.ty = self.word(); if !spec.ty.is_empty() { @@ -932,6 +943,30 @@ impl<'a> Parser<'a> { } } } + + fn suggest_format_parameter(&mut self, c: char) { + let replacement = match c { + '#' => "#?", + 'x' => "x?", + 'X' => "X?", + _ => return, + }; + let Some(pos) = self.consume_pos(c) else { + return; + }; + + let span = self.span(pos - 1, pos + 1); + let pos = self.to_span_index(pos); + + self.errors.insert(0, ParseError { + description: format!("expected `}}`, found `{c}`"), + note: None, + label: "expected `'}'`".into(), + span: pos.to(pos), + secondary_label: None, + suggestion: Suggestion::ReorderFormatParameter(span, format!("{replacement}")), + }) + } } /// Finds the indices of all characters that have been processed and differ between the actual diff --git a/tests/ui/fmt/suggest-wrongly-order-format-parameter.fixed b/tests/ui/fmt/suggest-wrongly-order-format-parameter.fixed new file mode 100644 index 0000000000000..a080a65854ab3 --- /dev/null +++ b/tests/ui/fmt/suggest-wrongly-order-format-parameter.fixed @@ -0,0 +1,25 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/129966 +//! +//! Ensure we provide suggestion for wrongly ordered format parameters. + +//@ run-rustfix +#![allow(dead_code)] + +#[derive(Debug)] +struct Foo(u8, u8); + +fn main() { + let f = Foo(1, 2); + + println!("{f:#?}"); + //~^ ERROR invalid format string: expected `}`, found `#` + //~| HELP did you mean `#?`? + + println!("{f:x?}"); + //~^ ERROR invalid format string: expected `}`, found `x` + //~| HELP did you mean `x?`? + + println!("{f:X?}"); + //~^ ERROR invalid format string: expected `}`, found `X` + //~| HELP did you mean `X?`? +} diff --git a/tests/ui/fmt/suggest-wrongly-order-format-parameter.rs b/tests/ui/fmt/suggest-wrongly-order-format-parameter.rs new file mode 100644 index 0000000000000..830dafd447908 --- /dev/null +++ b/tests/ui/fmt/suggest-wrongly-order-format-parameter.rs @@ -0,0 +1,25 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/129966 +//! +//! Ensure we provide suggestion for wrongly ordered format parameters. + +//@ run-rustfix +#![allow(dead_code)] + +#[derive(Debug)] +struct Foo(u8, u8); + +fn main() { + let f = Foo(1, 2); + + println!("{f:?#}"); + //~^ ERROR invalid format string: expected `}`, found `#` + //~| HELP did you mean `#?`? + + println!("{f:?x}"); + //~^ ERROR invalid format string: expected `}`, found `x` + //~| HELP did you mean `x?`? + + println!("{f:?X}"); + //~^ ERROR invalid format string: expected `}`, found `X` + //~| HELP did you mean `X?`? +} diff --git a/tests/ui/fmt/suggest-wrongly-order-format-parameter.stderr b/tests/ui/fmt/suggest-wrongly-order-format-parameter.stderr new file mode 100644 index 0000000000000..fe693d2e90410 --- /dev/null +++ b/tests/ui/fmt/suggest-wrongly-order-format-parameter.stderr @@ -0,0 +1,35 @@ +error: invalid format string: expected `}`, found `#` + --> $DIR/suggest-wrongly-order-format-parameter.rs:14:19 + | +LL | println!("{f:?#}"); + | ^ expected `'}'` in format string + | +help: did you mean `#?`? + | +LL | println!("{f:#?}"); + | ~~ + +error: invalid format string: expected `}`, found `x` + --> $DIR/suggest-wrongly-order-format-parameter.rs:18:19 + | +LL | println!("{f:?x}"); + | ^ expected `'}'` in format string + | +help: did you mean `x?`? + | +LL | println!("{f:x?}"); + | ~~ + +error: invalid format string: expected `}`, found `X` + --> $DIR/suggest-wrongly-order-format-parameter.rs:22:19 + | +LL | println!("{f:?X}"); + | ^ expected `'}'` in format string + | +help: did you mean `X?`? + | +LL | println!("{f:X?}"); + | ~~ + +error: aborting due to 3 previous errors +