Skip to content

Commit 4f6a26f

Browse files
committed
format_args: insert implicit named arguments by format trait also
This fixes #109576 by not using the same argument for implicit named arguments that have the same name but different traits.
1 parent 24a69af commit 4f6a26f

6 files changed

+116
-27
lines changed

compiler/rustc_ast/src/format.rs

+31-7
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub struct FormatArguments {
6565
num_unnamed_args: usize,
6666
num_explicit_args: usize,
6767
names: FxHashMap<Symbol, usize>,
68+
implicit_args: FxHashMap<(Symbol, FormatTrait), usize>,
6869
}
6970

7071
// FIXME: Rustdoc has trouble proving Send/Sync for this. See #106930.
@@ -78,20 +79,34 @@ impl FormatArguments {
7879
Self {
7980
arguments: Vec::new(),
8081
names: FxHashMap::default(),
82+
implicit_args: FxHashMap::default(),
8183
num_unnamed_args: 0,
8284
num_explicit_args: 0,
8385
}
8486
}
8587

86-
pub fn add(&mut self, arg: FormatArgument) -> usize {
88+
pub fn add(
89+
&mut self,
90+
arg: FormatArgument,
91+
implicit_arg_format_trait: Option<FormatTrait>,
92+
) -> usize {
8793
let index = self.arguments.len();
88-
if let Some(name) = arg.kind.ident() {
89-
self.names.insert(name.name, index);
90-
} else if self.names.is_empty() {
91-
// Only count the unnamed args before the first named arg.
92-
// (Any later ones are errors.)
93-
self.num_unnamed_args += 1;
94+
95+
match (arg.kind.ident(), implicit_arg_format_trait) {
96+
(Some(ident), Some(format_trait)) => {
97+
self.implicit_args.insert((ident.name, format_trait), index);
98+
}
99+
(Some(ident), None) => {
100+
self.names.insert(ident.name, index);
101+
}
102+
_ if self.names.is_empty() => {
103+
// Only count the unnamed args before the first named arg.
104+
// (Any later ones are errors.)
105+
self.num_unnamed_args += 1;
106+
}
107+
_ => {}
94108
}
109+
95110
if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
96111
// This is an explicit argument.
97112
// Make sure that all arguments so far are explcit.
@@ -111,6 +126,15 @@ impl FormatArguments {
111126
Some((i, &self.arguments[i]))
112127
}
113128

129+
pub fn by_implicit_arg(
130+
&self,
131+
name: Symbol,
132+
format_trait: FormatTrait,
133+
) -> Option<(usize, &FormatArgument)> {
134+
let i = *self.implicit_args.get(&(name, format_trait))?;
135+
Some((i, &self.arguments[i]))
136+
}
137+
114138
pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {
115139
(i < self.num_explicit_args).then(|| &self.arguments[i])
116140
}

compiler/rustc_builtin_macros/src/format.rs

+33-20
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<
130130
.emit();
131131
continue;
132132
}
133-
args.add(FormatArgument { kind: FormatArgumentKind::Named(ident), expr });
133+
args.add(FormatArgument { kind: FormatArgumentKind::Named(ident), expr }, None);
134134
}
135135
_ => {
136136
let expr = p.parse_expr()?;
@@ -150,7 +150,7 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<
150150
}
151151
err.emit();
152152
}
153-
args.add(FormatArgument { kind: FormatArgumentKind::Normal, expr });
153+
args.add(FormatArgument { kind: FormatArgumentKind::Normal, expr }, None);
154154
}
155155
}
156156
}
@@ -283,7 +283,8 @@ fn make_format_args(
283283
let mut lookup_arg = |arg: ArgRef<'_>,
284284
span: Option<Span>,
285285
used_as: PositionUsedAs,
286-
kind: FormatArgPositionKind|
286+
kind: FormatArgPositionKind,
287+
fmt_trait: Option<FormatTrait>|
287288
-> FormatArgPosition {
288289
let index = match arg {
289290
Index(index) => {
@@ -309,6 +310,9 @@ fn make_format_args(
309310
used[index] = true;
310311
}
311312
Ok(index)
313+
} else if let Some(tr) = fmt_trait
314+
&& let Some((index, _)) = args.by_implicit_arg(name, tr) {
315+
Ok(index)
312316
} else {
313317
// Name not found in `args`, so we add it as an implicitly captured argument.
314318
let span = span.unwrap_or(fmt_span);
@@ -324,7 +328,7 @@ fn make_format_args(
324328
.emit();
325329
DummyResult::raw_expr(span, true)
326330
};
327-
Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr }))
331+
Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr }, fmt_trait))
328332
}
329333
}
330334
};
@@ -350,24 +354,44 @@ fn make_format_args(
350354
placeholder_index += 1;
351355

352356
let position_span = to_span(position_span);
357+
358+
let format_trait = match format.ty {
359+
"" => FormatTrait::Display,
360+
"?" => FormatTrait::Debug,
361+
"e" => FormatTrait::LowerExp,
362+
"E" => FormatTrait::UpperExp,
363+
"o" => FormatTrait::Octal,
364+
"p" => FormatTrait::Pointer,
365+
"b" => FormatTrait::Binary,
366+
"x" => FormatTrait::LowerHex,
367+
"X" => FormatTrait::UpperHex,
368+
_ => {
369+
invalid_placeholder_type_error(ecx, format.ty, format.ty_span, fmt_span);
370+
FormatTrait::Display
371+
}
372+
};
373+
353374
let argument = match position {
354375
parse::ArgumentImplicitlyIs(i) => lookup_arg(
355376
Index(i),
356377
position_span,
357378
Placeholder(span),
358379
FormatArgPositionKind::Implicit,
380+
Some(format_trait),
359381
),
360382
parse::ArgumentIs(i) => lookup_arg(
361383
Index(i),
362384
position_span,
363385
Placeholder(span),
364386
FormatArgPositionKind::Number,
387+
Some(format_trait),
365388
),
366389
parse::ArgumentNamed(name) => lookup_arg(
367390
Name(name, position_span),
368391
position_span,
369392
Placeholder(span),
370393
FormatArgPositionKind::Named,
394+
Some(format_trait),
371395
),
372396
};
373397

@@ -378,22 +402,6 @@ fn make_format_args(
378402
parse::AlignCenter => Some(FormatAlignment::Center),
379403
};
380404

381-
let format_trait = match format.ty {
382-
"" => FormatTrait::Display,
383-
"?" => FormatTrait::Debug,
384-
"e" => FormatTrait::LowerExp,
385-
"E" => FormatTrait::UpperExp,
386-
"o" => FormatTrait::Octal,
387-
"p" => FormatTrait::Pointer,
388-
"b" => FormatTrait::Binary,
389-
"x" => FormatTrait::LowerHex,
390-
"X" => FormatTrait::UpperHex,
391-
_ => {
392-
invalid_placeholder_type_error(ecx, format.ty, format.ty_span, fmt_span);
393-
FormatTrait::Display
394-
}
395-
};
396-
397405
let precision_span = format.precision_span.and_then(to_span);
398406
let precision = match format.precision {
399407
parse::CountIs(n) => Some(FormatCount::Literal(n)),
@@ -402,18 +410,21 @@ fn make_format_args(
402410
precision_span,
403411
Precision,
404412
FormatArgPositionKind::Named,
413+
None,
405414
))),
406415
parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
407416
Index(i),
408417
precision_span,
409418
Precision,
410419
FormatArgPositionKind::Number,
420+
None,
411421
))),
412422
parse::CountIsStar(i) => Some(FormatCount::Argument(lookup_arg(
413423
Index(i),
414424
precision_span,
415425
Precision,
416426
FormatArgPositionKind::Implicit,
427+
None,
417428
))),
418429
parse::CountImplied => None,
419430
};
@@ -426,12 +437,14 @@ fn make_format_args(
426437
width_span,
427438
Width,
428439
FormatArgPositionKind::Named,
440+
None,
429441
))),
430442
parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
431443
Index(i),
432444
width_span,
433445
Width,
434446
FormatArgPositionKind::Number,
447+
None,
435448
))),
436449
parse::CountIsStar(_) => unreachable!(),
437450
parse::CountImplied => None,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: -Z unpretty=expanded
2+
// check-pass
3+
struct X;
4+
5+
fn main() {
6+
let x = X;
7+
println!("test: {x} {x:?}");
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(prelude_import)]
2+
#![no_std]
3+
#[prelude_import]
4+
use ::std::prelude::rust_2015::*;
5+
#[macro_use]
6+
extern crate std;
7+
// compile-flags: -Z unpretty=expanded
8+
// check-pass
9+
struct X;
10+
11+
fn main() {
12+
let x = X;
13+
{ ::std::io::_print(format_args!("test: {0} {1:?}\n", x, x)); };
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
struct X;
2+
3+
impl std::fmt::Display for X {
4+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5+
write!(f, "x")
6+
}
7+
}
8+
9+
fn main() {
10+
let x = X;
11+
println!("test: {x} {x:?}");
12+
//~^ ERROR: `X` doesn't implement `Debug`
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0277]: `X` doesn't implement `Debug`
2+
--> $DIR/format-args-point-to-correct-arg.rs:11:26
3+
|
4+
LL | println!("test: {x} {x:?}");
5+
| ^ `X` cannot be formatted using `{:?}`
6+
|
7+
= help: the trait `Debug` is not implemented for `X`
8+
= note: add `#[derive(Debug)]` to `X` or manually `impl Debug for X`
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+
help: consider annotating `X` with `#[derive(Debug)]`
11+
|
12+
LL | #[derive(Debug)]
13+
|
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)