Skip to content

Commit a3eb2f0

Browse files
authored
Rollup merge of #109664 - m-ou-se:format-args-placeholder-span, r=oli-obk
Use span of placeholders in format_args!() expansion. `format_args!("{}", x)` expands to something that contains `Argument::new_display(&x)`. That entire expression was generated with the span of `x`. After this PR, `&x` uses the span of `x`, but the `new_display` call uses the span of the `{}` placeholder within the format string. If an implicitly captured argument was used like in `format_args!("{x}")`, both use the span of the `{x}` placeholder. This fixes #109576, and also allows for more improvements to similar diagnostics in the future, since the usage of `x` can now be traced to the exact `{}` placeholder that required it to be `Display` (or `Debug` etc.)
2 parents 39f93d3 + 6c72a00 commit a3eb2f0

File tree

8 files changed

+153
-60
lines changed

8 files changed

+153
-60
lines changed

Diff for: compiler/rustc_ast_lowering/src/format.rs

+43-23
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::LoweringContext;
22
use rustc_ast as ast;
33
use rustc_ast::visit::{self, Visitor};
44
use rustc_ast::*;
5-
use rustc_data_structures::fx::FxIndexSet;
5+
use rustc_data_structures::fx::FxIndexMap;
66
use rustc_hir as hir;
77
use rustc_span::{
88
sym,
@@ -238,7 +238,7 @@ fn make_count<'hir>(
238238
ctx: &mut LoweringContext<'_, 'hir>,
239239
sp: Span,
240240
count: &Option<FormatCount>,
241-
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
241+
argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
242242
) -> hir::Expr<'hir> {
243243
match count {
244244
Some(FormatCount::Literal(n)) => {
@@ -252,7 +252,7 @@ fn make_count<'hir>(
252252
}
253253
Some(FormatCount::Argument(arg)) => {
254254
if let Ok(arg_index) = arg.index {
255-
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
255+
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize), arg.span);
256256
let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
257257
sp,
258258
hir::LangItem::FormatCount,
@@ -291,12 +291,14 @@ fn make_format_spec<'hir>(
291291
ctx: &mut LoweringContext<'_, 'hir>,
292292
sp: Span,
293293
placeholder: &FormatPlaceholder,
294-
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
294+
argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
295295
) -> hir::Expr<'hir> {
296296
let position = match placeholder.argument.index {
297297
Ok(arg_index) => {
298-
let (i, _) =
299-
argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
298+
let (i, _) = argmap.insert_full(
299+
(arg_index, ArgumentType::Format(placeholder.format_trait)),
300+
placeholder.span,
301+
);
300302
ctx.expr_usize(sp, i)
301303
}
302304
Err(_) => ctx.expr(
@@ -386,15 +388,18 @@ fn expand_format_args<'hir>(
386388

387389
// Create a list of all _unique_ (argument, format trait) combinations.
388390
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
389-
let mut argmap = FxIndexSet::default();
391+
let mut argmap = FxIndexMap::default();
390392
for piece in &fmt.template {
391393
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
392394
if placeholder.format_options != Default::default() {
393395
// Can't use basic form if there's any formatting options.
394396
use_format_options = true;
395397
}
396398
if let Ok(index) = placeholder.argument.index {
397-
if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
399+
if argmap
400+
.insert((index, ArgumentType::Format(placeholder.format_trait)), placeholder.span)
401+
.is_some()
402+
{
398403
// Duplicate (argument, format trait) combination,
399404
// which we'll only put once in the args array.
400405
use_format_options = true;
@@ -438,7 +443,7 @@ fn expand_format_args<'hir>(
438443
// This is an optimization, speeding up compilation about 1-2% in some cases.
439444
// See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
440445
let use_simple_array = argmap.len() == arguments.len()
441-
&& argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
446+
&& argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
442447
&& arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
443448

444449
let args = if use_simple_array {
@@ -452,14 +457,19 @@ fn expand_format_args<'hir>(
452457
let elements: Vec<_> = arguments
453458
.iter()
454459
.zip(argmap)
455-
.map(|(arg, (_, ty))| {
456-
let sp = arg.expr.span.with_ctxt(macsp.ctxt());
460+
.map(|(arg, ((_, ty), placeholder_span))| {
461+
let placeholder_span =
462+
placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
463+
let arg_span = match arg.kind {
464+
FormatArgumentKind::Captured(_) => placeholder_span,
465+
_ => arg.expr.span.with_ctxt(macsp.ctxt()),
466+
};
457467
let arg = ctx.lower_expr(&arg.expr);
458468
let ref_arg = ctx.arena.alloc(ctx.expr(
459-
sp,
469+
arg_span,
460470
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
461471
));
462-
make_argument(ctx, sp, ref_arg, ty)
472+
make_argument(ctx, placeholder_span, ref_arg, ty)
463473
})
464474
.collect();
465475
ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
@@ -475,16 +485,26 @@ fn expand_format_args<'hir>(
475485
// }
476486
let args_ident = Ident::new(sym::args, macsp);
477487
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
478-
let args = ctx.arena.alloc_from_iter(argmap.iter().map(|&(arg_index, ty)| {
479-
let arg = &arguments[arg_index];
480-
let sp = arg.expr.span.with_ctxt(macsp.ctxt());
481-
let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
482-
let arg = ctx.arena.alloc(ctx.expr(
483-
sp,
484-
hir::ExprKind::Field(args_ident_expr, Ident::new(sym::integer(arg_index), macsp)),
485-
));
486-
make_argument(ctx, sp, arg, ty)
487-
}));
488+
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
489+
|(&(arg_index, ty), &placeholder_span)| {
490+
let arg = &arguments[arg_index];
491+
let placeholder_span =
492+
placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
493+
let arg_span = match arg.kind {
494+
FormatArgumentKind::Captured(_) => placeholder_span,
495+
_ => arg.expr.span.with_ctxt(macsp.ctxt()),
496+
};
497+
let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
498+
let arg = ctx.arena.alloc(ctx.expr(
499+
arg_span,
500+
hir::ExprKind::Field(
501+
args_ident_expr,
502+
Ident::new(sym::integer(arg_index), macsp),
503+
),
504+
));
505+
make_argument(ctx, placeholder_span, arg, ty)
506+
},
507+
));
488508
let elements: Vec<_> = arguments
489509
.iter()
490510
.map(|arg| {

Diff for: tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff

+22-22
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
let mut _17: &[core::fmt::ArgumentV1<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL
2020
let _18: &[core::fmt::ArgumentV1<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL
2121
let _19: [core::fmt::ArgumentV1<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL
22-
let mut _20: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:21: +10:22
23-
let mut _21: &std::boxed::Box<dyn std::fmt::Display>; // in scope 0 at $DIR/lifetimes.rs:+10:21: +10:22
24-
let _22: &std::boxed::Box<dyn std::fmt::Display>; // in scope 0 at $DIR/lifetimes.rs:+10:21: +10:22
25-
let mut _23: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:25: +10:26
26-
let mut _24: &u32; // in scope 0 at $DIR/lifetimes.rs:+10:25: +10:26
27-
let _25: &u32; // in scope 0 at $DIR/lifetimes.rs:+10:25: +10:26
22+
let mut _20: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23
23+
let mut _21: &std::boxed::Box<dyn std::fmt::Display>; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23
24+
let _22: &std::boxed::Box<dyn std::fmt::Display>; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23
25+
let mut _23: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27
26+
let mut _24: &u32; // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27
27+
let _25: &u32; // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27
2828
let mut _27: bool; // in scope 0 at $DIR/lifetimes.rs:+12:1: +12:2
2929
let mut _28: isize; // in scope 0 at $DIR/lifetimes.rs:+12:1: +12:2
3030
let mut _29: isize; // in scope 0 at $DIR/lifetimes.rs:+12:1: +12:2
@@ -108,34 +108,34 @@
108108
StorageLive(_17); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
109109
StorageLive(_18); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
110110
StorageLive(_19); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
111-
StorageLive(_20); // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
112-
StorageLive(_21); // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
113-
StorageLive(_22); // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
114-
_22 = &_8; // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
115-
_21 = &(*_22); // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
116-
_20 = core::fmt::ArgumentV1::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _21) -> bb3; // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
111+
StorageLive(_20); // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
112+
StorageLive(_21); // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
113+
StorageLive(_22); // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
114+
_22 = &_8; // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
115+
_21 = &(*_22); // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
116+
_20 = core::fmt::ArgumentV1::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _21) -> bb3; // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23
117117
// mir::Constant
118-
// + span: $DIR/lifetimes.rs:27:21: 27:22
118+
// + span: $DIR/lifetimes.rs:27:20: 27:23
119119
// + user_ty: UserType(4)
120120
// + literal: Const { ty: for<'b> fn(&'b Box<dyn std::fmt::Display>) -> core::fmt::ArgumentV1<'b> {core::fmt::ArgumentV1::<'_>::new_display::<Box<dyn std::fmt::Display>>}, val: Value(<ZST>) }
121121
}
122122

123123
bb3: {
124-
StorageDead(_21); // scope 4 at $DIR/lifetimes.rs:+10:21: +10:22
125-
StorageLive(_23); // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
126-
StorageLive(_24); // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
127-
StorageLive(_25); // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
128-
_25 = &_6; // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
129-
_24 = &(*_25); // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
130-
_23 = core::fmt::ArgumentV1::<'_>::new_display::<u32>(move _24) -> bb4; // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
124+
StorageDead(_21); // scope 4 at $DIR/lifetimes.rs:+10:22: +10:23
125+
StorageLive(_23); // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
126+
StorageLive(_24); // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
127+
StorageLive(_25); // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
128+
_25 = &_6; // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
129+
_24 = &(*_25); // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
130+
_23 = core::fmt::ArgumentV1::<'_>::new_display::<u32>(move _24) -> bb4; // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27
131131
// mir::Constant
132-
// + span: $DIR/lifetimes.rs:27:25: 27:26
132+
// + span: $DIR/lifetimes.rs:27:24: 27:27
133133
// + user_ty: UserType(5)
134134
// + literal: Const { ty: for<'b> fn(&'b u32) -> core::fmt::ArgumentV1<'b> {core::fmt::ArgumentV1::<'_>::new_display::<u32>}, val: Value(<ZST>) }
135135
}
136136

137137
bb4: {
138-
StorageDead(_24); // scope 4 at $DIR/lifetimes.rs:+10:25: +10:26
138+
StorageDead(_24); // scope 4 at $DIR/lifetimes.rs:+10:26: +10:27
139139
_19 = [move _20, move _23]; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
140140
StorageDead(_23); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
141141
StorageDead(_20); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL

Diff for: tests/ui/consts/const-eval/format.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0015]: cannot call non-const formatting macro in constant functions
2-
--> $DIR/format.rs:2:20
2+
--> $DIR/format.rs:2:13
33
|
44
LL | panic!("{:?}", 0);
5-
| ^
5+
| ^^^^
66
|
77
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
88
= note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -17,10 +17,10 @@ LL | panic!("{:?}", 0);
1717
= note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
1818

1919
error[E0015]: cannot call non-const formatting macro in constant functions
20-
--> $DIR/format.rs:8:22
20+
--> $DIR/format.rs:8:15
2121
|
2222
LL | println!("{:?}", 0);
23-
| ^
23+
| ^^^^
2424
|
2525
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
2626
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

Diff for: tests/ui/fmt/format-args-argument-span.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// check-compile
2+
3+
struct DisplayOnly;
4+
5+
impl std::fmt::Display for DisplayOnly {
6+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7+
unimplemented!()
8+
}
9+
}
10+
11+
fn main() {
12+
let x = Some(1);
13+
println!("{x:?} {x} {x:?}");
14+
//~^ ERROR: `Option<{integer}>` doesn't implement `std::fmt::Display`
15+
println!("{x:?} {x} {x:?}", x = Some(1));
16+
//~^ ERROR: `Option<{integer}>` doesn't implement `std::fmt::Display`
17+
let x = DisplayOnly;
18+
println!("{x} {x:?} {x}");
19+
//~^ ERROR: `DisplayOnly` doesn't implement `Debug`
20+
println!("{x} {x:?} {x}", x = DisplayOnly);
21+
//~^ ERROR: `DisplayOnly` doesn't implement `Debug`
22+
}

Diff for: tests/ui/fmt/format-args-argument-span.stderr

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
error[E0277]: `Option<{integer}>` doesn't implement `std::fmt::Display`
2+
--> $DIR/format-args-argument-span.rs:13:21
3+
|
4+
LL | println!("{x:?} {x} {x:?}");
5+
| ^^^ `Option<{integer}>` cannot be formatted with the default formatter
6+
|
7+
= help: the trait `std::fmt::Display` is not implemented for `Option<{integer}>`
8+
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
9+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
10+
11+
error[E0277]: `Option<{integer}>` doesn't implement `std::fmt::Display`
12+
--> $DIR/format-args-argument-span.rs:15:37
13+
|
14+
LL | println!("{x:?} {x} {x:?}", x = Some(1));
15+
| ^^^^^^^ `Option<{integer}>` cannot be formatted with the default formatter
16+
|
17+
= help: the trait `std::fmt::Display` is not implemented for `Option<{integer}>`
18+
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
19+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
20+
21+
error[E0277]: `DisplayOnly` doesn't implement `Debug`
22+
--> $DIR/format-args-argument-span.rs:18:19
23+
|
24+
LL | println!("{x} {x:?} {x}");
25+
| ^^^^^ `DisplayOnly` cannot be formatted using `{:?}`
26+
|
27+
= help: the trait `Debug` is not implemented for `DisplayOnly`
28+
= note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly`
29+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
30+
help: consider annotating `DisplayOnly` with `#[derive(Debug)]`
31+
|
32+
LL | #[derive(Debug)]
33+
|
34+
35+
error[E0277]: `DisplayOnly` doesn't implement `Debug`
36+
--> $DIR/format-args-argument-span.rs:20:35
37+
|
38+
LL | println!("{x} {x:?} {x}", x = DisplayOnly);
39+
| ^^^^^^^^^^^ `DisplayOnly` cannot be formatted using `{:?}`
40+
|
41+
= help: the trait `Debug` is not implemented for `DisplayOnly`
42+
= note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly`
43+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
44+
help: consider annotating `DisplayOnly` with `#[derive(Debug)]`
45+
|
46+
LL | #[derive(Debug)]
47+
|
48+
49+
error: aborting due to 4 previous errors
50+
51+
For more information about this error, try `rustc --explain E0277`.

Diff for: tests/ui/fmt/ifmt-bad-arg.stderr

+6-8
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,9 @@ error[E0308]: mismatched types
300300
--> $DIR/ifmt-bad-arg.rs:78:32
301301
|
302302
LL | println!("{} {:.*} {}", 1, 3.2, 4);
303-
| ^^^
304-
| |
305-
| expected `&usize`, found `&{float}`
306-
| arguments to this function are incorrect
303+
| -- ^^^ expected `&usize`, found `&{float}`
304+
| |
305+
| arguments to this function are incorrect
307306
|
308307
= note: expected reference `&usize`
309308
found reference `&{float}`
@@ -315,10 +314,9 @@ error[E0308]: mismatched types
315314
--> $DIR/ifmt-bad-arg.rs:81:35
316315
|
317316
LL | println!("{} {:07$.*} {}", 1, 3.2, 4);
318-
| ^^^
319-
| |
320-
| expected `&usize`, found `&{float}`
321-
| arguments to this function are incorrect
317+
| -- ^^^ expected `&usize`, found `&{float}`
318+
| |
319+
| arguments to this function are incorrect
322320
|
323321
= note: expected reference `&usize`
324322
found reference `&{float}`

Diff for: tests/ui/fmt/ifmt-unimpl.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ error[E0277]: the trait bound `str: UpperHex` is not satisfied
22
--> $DIR/ifmt-unimpl.rs:2:21
33
|
44
LL | format!("{:X}", "3");
5-
| ^^^ the trait `UpperHex` is not implemented for `str`
5+
| ---- ^^^ the trait `UpperHex` is not implemented for `str`
6+
| |
7+
| required by a bound introduced by this call
68
|
79
= help: the following other types implement trait `UpperHex`:
810
&T

Diff for: tests/ui/suggestions/issue-97760.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0277]: `<impl IntoIterator as IntoIterator>::Item` doesn't implement `std::fmt::Display`
2-
--> $DIR/issue-97760.rs:4:20
2+
--> $DIR/issue-97760.rs:4:19
33
|
44
LL | println!("{x}");
5-
| ^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
5+
| ^^^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
66
|
77
= help: the trait `std::fmt::Display` is not implemented for `<impl IntoIterator as IntoIterator>::Item`
88
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead

0 commit comments

Comments
 (0)