Skip to content

Commit 86f4f68

Browse files
committed
Improve handling of invalid references in format!()
1 parent 159dcb2 commit 86f4f68

File tree

4 files changed

+82
-18
lines changed

4 files changed

+82
-18
lines changed

src/libsyntax_ext/format.rs

+34-13
Original file line numberDiff line numberDiff line change
@@ -275,19 +275,18 @@ impl<'a, 'b> Context<'a, 'b> {
275275
} else {
276276
MultiSpan::from_span(self.fmtsp)
277277
};
278-
let refs_len = self.invalid_refs.len();
279-
let mut refs = self
278+
let refs = self
280279
.invalid_refs
281280
.iter()
282281
.map(|(r, pos)| (r.to_string(), self.arg_spans.get(*pos)));
283282

284283
let mut zero_based_note = false;
285284

286-
if self.names.is_empty() && !numbered_position_args {
287-
let count = self.pieces.len() + self.arg_with_formatting
288-
.iter()
289-
.filter(|fmt| fmt.precision_span.is_some())
290-
.count();
285+
let count = self.pieces.len() + self.arg_with_formatting
286+
.iter()
287+
.filter(|fmt| fmt.precision_span.is_some())
288+
.count();
289+
if self.names.is_empty() && !numbered_position_args && count != self.args.len() {
291290
e = self.ecx.mut_span_err(
292291
sp,
293292
&format!(
@@ -298,14 +297,22 @@ impl<'a, 'b> Context<'a, 'b> {
298297
),
299298
);
300299
} else {
301-
let (arg_list, mut sp) = if refs_len == 1 {
302-
let (reg, pos) = refs.next().unwrap();
300+
let (mut refs, spans): (Vec<_>, Vec<_>) = refs.unzip();
301+
// Avoid `invalid reference to positional arguments 7 and 7 (there is 1 argument)`
302+
// for `println!("{7:7$}", 1);`
303+
refs.dedup();
304+
refs.sort();
305+
let (arg_list, mut sp) = if refs.len() == 1 {
306+
let spans: Vec<_> = spans.into_iter().filter_map(|sp| sp.map(|sp| *sp)).collect();
303307
(
304-
format!("argument {}", reg),
305-
MultiSpan::from_span(*pos.unwrap_or(&self.fmtsp)),
308+
format!("argument {}", refs[0]),
309+
if spans.is_empty() {
310+
MultiSpan::from_span(self.fmtsp)
311+
} else {
312+
MultiSpan::from_spans(spans)
313+
},
306314
)
307315
} else {
308-
let (mut refs, spans): (Vec<_>, Vec<_>) = refs.unzip();
309316
let pos = MultiSpan::from_spans(spans.into_iter().map(|s| *s.unwrap()).collect());
310317
let reg = refs.pop().unwrap();
311318
(
@@ -754,7 +761,21 @@ impl<'a, 'b> Context<'a, 'b> {
754761
"x" => "LowerHex",
755762
"X" => "UpperHex",
756763
_ => {
757-
ecx.span_err(sp, &format!("unknown format trait `{}`", *tyname));
764+
let mut err = ecx.struct_span_err(
765+
sp,
766+
&format!("unknown format trait `{}`", *tyname),
767+
);
768+
err.note("the only appropriate formatting traits are:\n\
769+
- ``, which uses the `Display` trait\n\
770+
- `?`, which uses the `Debug` trait\n\
771+
- `e`, which uses the `LowerExp` trait\n\
772+
- `E`, which uses the `UpperExp` trait\n\
773+
- `o`, which uses the `Octal` trait\n\
774+
- `p`, which uses the `Pointer` trait\n\
775+
- `b`, which uses the `Binary` trait\n\
776+
- `x`, which uses the `LowerHex` trait\n\
777+
- `X`, which uses the `UpperHex` trait");
778+
err.emit();
758779
return DummyResult::raw_expr(sp, true);
759780
}
760781
}

src/test/ui/if/ifmt-bad-arg.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,8 @@ tenth number: {}",
8282
//~^ ERROR 4 positional arguments in format string, but there are 3 arguments
8383
//~| ERROR mismatched types
8484
println!("{} {:07$} {}", 1, 3.2, 4);
85-
//~^ ERROR 3 positional arguments in format string, but there are 3 arguments
85+
//~^ ERROR invalid reference to positional argument 7 (there are 3 arguments)
86+
println!("{:foo}", 1); //~ ERROR unknown format trait `foo`
87+
println!("{5} {:4$} {6:7$}", 1);
88+
//~^ ERROR invalid reference to positional arguments 4, 5, 6 and 7 (there is 1 argument)
8689
}

src/test/ui/if/ifmt-bad-arg.stderr

+33-4
Original file line numberDiff line numberDiff line change
@@ -243,17 +243,46 @@ LL | println!("{} {:07$.*} {}", 1, 3.2, 4);
243243
= note: positional arguments are zero-based
244244
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
245245

246-
error: 3 positional arguments in format string, but there are 3 arguments
247-
--> $DIR/ifmt-bad-arg.rs:84:15
246+
error: invalid reference to positional argument 7 (there are 3 arguments)
247+
--> $DIR/ifmt-bad-arg.rs:84:18
248248
|
249249
LL | println!("{} {:07$} {}", 1, 3.2, 4);
250-
| ^^ ^^---^ ^^
250+
| ^^---^
251251
| |
252252
| this width flag expects an `usize` argument at position 7, but there are 3 arguments
253253
|
254254
= note: positional arguments are zero-based
255255
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
256256

257+
error: unknown format trait `foo`
258+
--> $DIR/ifmt-bad-arg.rs:86:24
259+
|
260+
LL | println!("{:foo}", 1);
261+
| ^
262+
|
263+
= note: the only appropriate formatting traits are:
264+
- ``, which uses the `Display` trait
265+
- `?`, which uses the `Debug` trait
266+
- `e`, which uses the `LowerExp` trait
267+
- `E`, which uses the `UpperExp` trait
268+
- `o`, which uses the `Octal` trait
269+
- `p`, which uses the `Pointer` trait
270+
- `b`, which uses the `Binary` trait
271+
- `x`, which uses the `LowerHex` trait
272+
- `X`, which uses the `UpperHex` trait
273+
274+
error: invalid reference to positional arguments 4, 5, 6 and 7 (there is 1 argument)
275+
--> $DIR/ifmt-bad-arg.rs:87:15
276+
|
277+
LL | println!("{5} {:4$} {6:7$}", 1);
278+
| ^^^ ^^--^ ^^^--^
279+
| | |
280+
| | this width flag expects an `usize` argument at position 7, but there is 1 argument
281+
| this width flag expects an `usize` argument at position 4, but there is 1 argument
282+
|
283+
= note: positional arguments are zero-based
284+
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
285+
257286
error[E0308]: mismatched types
258287
--> $DIR/ifmt-bad-arg.rs:78:32
259288
|
@@ -272,6 +301,6 @@ LL | println!("{} {:07$.*} {}", 1, 3.2, 4);
272301
= note: expected type `&usize`
273302
found type `&{float}`
274303

275-
error: aborting due to 33 previous errors
304+
error: aborting due to 35 previous errors
276305

277306
For more information about this error, try `rustc --explain E0308`.

src/test/ui/if/ifmt-unknown-trait.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ error: unknown format trait `notimplemented`
33
|
44
LL | format!("{:notimplemented}", "3");
55
| ^^^
6+
|
7+
= note: the only appropriate formatting traits are:
8+
- ``, which uses the `Display` trait
9+
- `?`, which uses the `Debug` trait
10+
- `e`, which uses the `LowerExp` trait
11+
- `E`, which uses the `UpperExp` trait
12+
- `o`, which uses the `Octal` trait
13+
- `p`, which uses the `Pointer` trait
14+
- `b`, which uses the `Binary` trait
15+
- `x`, which uses the `LowerHex` trait
16+
- `X`, which uses the `UpperHex` trait
617

718
error: aborting due to previous error
819

0 commit comments

Comments
 (0)