rustc_parse_format: improve diagnostics for unsupported debug = syntax

Detect Python-style f-string debug syntax in format strings and emit a
clear diagnostic explaining that it is not supported in Rust. When the
intended operation can be inferred, suggest the corresponding Rust
alternative e.g from `println!("{=}", x)` to `dbg!({x})`.

Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
This commit is contained in:
Usman Akinyemi 2026-01-14 11:03:45 +05:30
parent 66daca1a85
commit 0061a2232d
8 changed files with 97 additions and 2 deletions

View file

@ -678,6 +678,18 @@ pub(crate) enum InvalidFormatStringSuggestion {
#[primary_span]
span: Span,
},
#[suggestion(
"use rust debug printing macro",
code = "{replacement}",
style = "verbose",
applicability = "machine-applicable"
)]
UseRustDebugPrintingMacro {
#[primary_span]
macro_span: Span,
replacement: String,
},
}
#[derive(Diagnostic)]

View file

@ -160,6 +160,7 @@ fn make_format_args(
ecx: &mut ExtCtxt<'_>,
input: MacroInput,
append_newline: bool,
macro_span: Span,
) -> ExpandResult<Result<FormatArgs, ErrorGuaranteed>, ()> {
let msg = "format argument must be a string literal";
let unexpanded_fmt_span = input.fmtstr.span;
@ -333,6 +334,23 @@ fn make_format_args(
let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end));
e.sugg_ = Some(errors::InvalidFormatStringSuggestion::AddMissingColon { span });
}
parse::Suggestion::UseRustDebugPrintingMacro => {
// This targets `println!("{=}", x);` and `println!("{0=}", x);`
if let [arg] = args.all_args() {
let expr_span = arg.expr.span;
if let Ok(expr_snippet) = ecx.source_map().span_to_snippet(expr_span) {
let replacement = format!("{}!({})", "dbg", expr_snippet);
let call_span = macro_span.source_callsite();
e.sugg_ = Some(
errors::InvalidFormatStringSuggestion::UseRustDebugPrintingMacro {
macro_span: call_span,
replacement,
},
);
}
}
}
}
let guar = ecx.dcx().emit_err(e);
return ExpandResult::Ready(Err(guar));
@ -1048,7 +1066,7 @@ fn expand_format_args_impl<'cx>(
sp = ecx.with_def_site_ctxt(sp);
ExpandResult::Ready(match parse_args(ecx, sp, tts) {
Ok(input) => {
let ExpandResult::Ready(mac) = make_format_args(ecx, input, nl) else {
let ExpandResult::Ready(mac) = make_format_args(ecx, input, nl, sp) else {
return ExpandResult::Retry(());
};
match mac {

View file

@ -187,6 +187,9 @@ pub enum Suggestion {
/// Add missing colon:
/// `format!("{foo?}")` -> `format!("{foo:?}")`
AddMissingColon(Range<usize>),
/// Use Rust format string:
/// `format!("{x=}")` -> `dbg!(x)`
UseRustDebugPrintingMacro,
}
/// The parser structure for interpreting the input format string. This is
@ -462,6 +465,7 @@ impl<'input> Parser<'input> {
('?', _) => self.suggest_format_debug(),
('<' | '^' | '>', _) => self.suggest_format_align(c),
(',', _) => self.suggest_unsupported_python_numeric_grouping(),
('=', '}') => self.suggest_rust_debug_printing_macro(),
_ => self.suggest_positional_arg_instead_of_captured_arg(arg),
}
}
@ -871,6 +875,27 @@ impl<'input> Parser<'input> {
}
}
fn suggest_rust_debug_printing_macro(&mut self) {
if let Some((range, _)) = self.consume_pos('=') {
self.errors.insert(
0,
ParseError {
description:
"python's f-string debug `=` is not supported in rust, use `dbg(x)` instead"
.to_owned(),
note: Some(format!("to print `{{`, you can escape it using `{{{{`",)),
label: "expected `}`".to_owned(),
span: range,
secondary_label: self
.last_open_brace
.clone()
.map(|sp| ("because of this opening brace".to_owned(), sp)),
suggestion: Suggestion::UseRustDebugPrintingMacro,
},
);
}
}
fn suggest_format_align(&mut self, alignment: char) {
if let Some((range, _)) = self.consume_pos(alignment) {
self.errors.insert(

View file

@ -88,4 +88,7 @@ raw { \n
//~^ ERROR invalid format string: expected `}`, found `?`
println!("{x,}, world!",);
//~^ ERROR invalid format string: python's numeric grouping `,` is not supported in rust format strings
println!("{x=}");
//~^ ERROR invalid format string: python's f-string debug `=` is not supported in rust, use `dbg(x)` instead
}

View file

@ -198,5 +198,15 @@ LL | println!("{x,}, world!",);
|
= note: to print `{`, you can escape it using `{{`
error: aborting due to 20 previous errors
error: invalid format string: python's f-string debug `=` is not supported in rust, use `dbg(x)` instead
--> $DIR/format-string-error-2.rs:92:17
|
LL | println!("{x=}");
| - ^ expected `}` in format string
| |
| because of this opening brace
|
= note: to print `{`, you can escape it using `{{`
error: aborting due to 21 previous errors

View file

@ -0,0 +1,5 @@
//@ run-rustfix
fn main() {
let x = 32;
dbg!(x); //~ ERROR invalid format string: python's f-string debug
}

View file

@ -0,0 +1,5 @@
//@ run-rustfix
fn main() {
let x = 32;
println!("{=}", x); //~ ERROR invalid format string: python's f-string debug
}

View file

@ -0,0 +1,17 @@
error: invalid format string: python's f-string debug `=` is not supported in rust, use `dbg(x)` instead
--> $DIR/format-string-error-3.rs:4:16
|
LL | println!("{=}", x);
| -^ expected `}` in format string
| |
| because of this opening brace
|
= note: to print `{`, you can escape it using `{{`
help: use rust debug printing macro
|
LL - println!("{=}", x);
LL + dbg!(x);
|
error: aborting due to 1 previous error