Skip to content

Commit 38abca8

Browse files
author
Esteban Küber
committed
Point at internal span in format string
1 parent d3b3bc5 commit 38abca8

File tree

5 files changed

+224
-18
lines changed

5 files changed

+224
-18
lines changed

src/libfmt_macros/lib.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ pub struct Parser<'a> {
154154
style: Option<usize>,
155155
/// How many newlines have been seen in the string so far, to adjust the error spans
156156
seen_newlines: usize,
157+
pub arg_places: Vec<(usize, usize)>,
157158
}
158159

159160
impl<'a> Iterator for Parser<'a> {
@@ -168,9 +169,13 @@ impl<'a> Iterator for Parser<'a> {
168169
if self.consume('{') {
169170
Some(String(self.string(pos + 1)))
170171
} else {
171-
let ret = Some(NextArgument(self.argument()));
172-
self.must_consume('}');
173-
ret
172+
let mut arg = self.argument();
173+
if let Some(arg_pos) = self.must_consume('}').map(|end| {
174+
(pos + raw + 1, end + raw + 2)
175+
}) {
176+
self.arg_places.push(arg_pos);
177+
}
178+
Some(NextArgument(arg))
174179
}
175180
}
176181
'}' => {
@@ -211,6 +216,7 @@ impl<'a> Parser<'a> {
211216
curarg: 0,
212217
style,
213218
seen_newlines: 0,
219+
arg_places: vec![],
214220
}
215221
}
216222

@@ -271,20 +277,22 @@ impl<'a> Parser<'a> {
271277

272278
/// Forces consumption of the specified character. If the character is not
273279
/// found, an error is emitted.
274-
fn must_consume(&mut self, c: char) {
280+
fn must_consume(&mut self, c: char) -> Option<usize> {
275281
self.ws();
276282
let raw = self.style.unwrap_or(0);
277283

278284
let padding = raw + self.seen_newlines;
279285
if let Some(&(pos, maybe)) = self.cur.peek() {
280286
if c == maybe {
281287
self.cur.next();
288+
Some(pos)
282289
} else {
283290
let pos = pos + padding + 1;
284291
self.err(format!("expected `{:?}`, found `{:?}`", c, maybe),
285292
format!("expected `{}`", c),
286293
pos,
287294
pos);
295+
None
288296
}
289297
} else {
290298
let msg = format!("expected `{:?}` but string was terminated", c);
@@ -302,6 +310,7 @@ impl<'a> Parser<'a> {
302310
} else {
303311
self.err(msg, format!("expected `{:?}`", c), pos, pos);
304312
}
313+
None
305314
}
306315
}
307316

src/libsyntax_ext/format.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use syntax::feature_gate;
2121
use syntax::parse::token;
2222
use syntax::ptr::P;
2323
use syntax::symbol::Symbol;
24-
use syntax_pos::{Span, DUMMY_SP};
24+
use syntax_pos::{Span, MultiSpan, DUMMY_SP};
2525
use syntax::tokenstream;
2626

2727
use std::collections::{HashMap, HashSet};
@@ -264,28 +264,38 @@ impl<'a, 'b> Context<'a, 'b> {
264264
/// errors for the case where all arguments are positional and for when
265265
/// there are named arguments or numbered positional arguments in the
266266
/// format string.
267-
fn report_invalid_references(&self, numbered_position_args: bool) {
267+
fn report_invalid_references(&self, numbered_position_args: bool, arg_places: &[(usize, usize)]) {
268268
let mut e;
269-
let mut refs: Vec<String> = self.invalid_refs
270-
.iter()
271-
.map(|r| r.to_string())
272-
.collect();
269+
let sps = arg_places.iter()
270+
.map(|&(start, end)| self.fmtsp.from_inner_byte_pos(start, end))
271+
.collect::<Vec<_>>();
272+
let sp = MultiSpan::from_spans(sps);
273+
let mut refs: Vec<_> = self.invalid_refs
274+
.iter()
275+
.map(|r| r.to_string())
276+
.collect();
273277

274278
if self.names.is_empty() && !numbered_position_args {
275-
e = self.ecx.mut_span_err(self.fmtsp,
279+
e = self.ecx.mut_span_err(sp,
276280
&format!("{} positional argument{} in format string, but {}",
277281
self.pieces.len(),
278282
if self.pieces.len() > 1 { "s" } else { "" },
279283
self.describe_num_args()));
280284
} else {
281285
let arg_list = match refs.len() {
282-
1 => format!("argument {}", refs.pop().unwrap()),
283-
_ => format!("arguments {head} and {tail}",
284-
tail=refs.pop().unwrap(),
286+
1 => {
287+
let reg = refs.pop().unwrap();
288+
format!("argument {}", reg)
289+
},
290+
_ => {
291+
let reg = refs.pop().unwrap();
292+
format!("arguments {head} and {tail}",
293+
tail=reg,
285294
head=refs.join(", "))
295+
}
286296
};
287297

288-
e = self.ecx.mut_span_err(self.fmtsp,
298+
e = self.ecx.mut_span_err(sp,
289299
&format!("invalid reference to positional {} ({})",
290300
arg_list,
291301
self.describe_num_args()));
@@ -835,7 +845,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
835845
}
836846

837847
if cx.invalid_refs.len() >= 1 {
838-
cx.report_invalid_references(numbered_position_args);
848+
cx.report_invalid_references(numbered_position_args, &parser.arg_places);
839849
}
840850

841851
// Make sure that all arguments were used and all arguments have types.
File renamed without changes.

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

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
error: 1 positional argument in format string, but no arguments were given
2+
--> $DIR/ifmt-bad-arg.rs:16:14
3+
|
4+
LL | format!("{}");
5+
| ^^
6+
7+
error: invalid reference to positional argument 1 (there is 1 argument)
8+
--> $DIR/ifmt-bad-arg.rs:19:14
9+
|
10+
LL | format!("{1}", 1);
11+
| ^^^
12+
|
13+
= note: positional arguments are zero-based
14+
15+
error: argument never used
16+
--> $DIR/ifmt-bad-arg.rs:19:20
17+
|
18+
LL | format!("{1}", 1);
19+
| ^
20+
21+
error: 2 positional arguments in format string, but no arguments were given
22+
--> $DIR/ifmt-bad-arg.rs:23:14
23+
|
24+
LL | format!("{} {}");
25+
| ^^ ^^
26+
27+
error: invalid reference to positional argument 1 (there is 1 argument)
28+
--> $DIR/ifmt-bad-arg.rs:26:14
29+
|
30+
LL | format!("{0} {1}", 1);
31+
| ^^^ ^^^
32+
|
33+
= note: positional arguments are zero-based
34+
35+
error: invalid reference to positional argument 2 (there are 2 arguments)
36+
--> $DIR/ifmt-bad-arg.rs:29:14
37+
|
38+
LL | format!("{0} {1} {2}", 1, 2);
39+
| ^^^ ^^^ ^^^
40+
|
41+
= note: positional arguments are zero-based
42+
43+
error: invalid reference to positional argument 2 (there are 2 arguments)
44+
--> $DIR/ifmt-bad-arg.rs:32:14
45+
|
46+
LL | format!("{} {value} {} {}", 1, value=2);
47+
| ^^ ^^^^^^^ ^^ ^^
48+
|
49+
= note: positional arguments are zero-based
50+
51+
error: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments)
52+
--> $DIR/ifmt-bad-arg.rs:34:14
53+
|
54+
LL | format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2);
55+
| ^^^^^^ ^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^
56+
|
57+
= note: positional arguments are zero-based
58+
59+
error: there is no argument named `foo`
60+
--> $DIR/ifmt-bad-arg.rs:37:13
61+
|
62+
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
63+
| ^^^^^^^^^^^^^^^^^^^^^^
64+
65+
error: there is no argument named `bar`
66+
--> $DIR/ifmt-bad-arg.rs:37:13
67+
|
68+
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
69+
| ^^^^^^^^^^^^^^^^^^^^^^
70+
71+
error: there is no argument named `foo`
72+
--> $DIR/ifmt-bad-arg.rs:41:13
73+
|
74+
LL | format!("{foo}"); //~ ERROR: no argument named `foo`
75+
| ^^^^^^^
76+
77+
error: multiple unused formatting arguments
78+
--> $DIR/ifmt-bad-arg.rs:42:17
79+
|
80+
LL | format!("", 1, 2); //~ ERROR: multiple unused formatting arguments
81+
| -- ^ ^
82+
| |
83+
| multiple missing formatting arguments
84+
85+
error: argument never used
86+
--> $DIR/ifmt-bad-arg.rs:43:22
87+
|
88+
LL | format!("{}", 1, 2); //~ ERROR: argument never used
89+
| ^
90+
91+
error: argument never used
92+
--> $DIR/ifmt-bad-arg.rs:44:20
93+
|
94+
LL | format!("{1}", 1, 2); //~ ERROR: argument never used
95+
| ^
96+
97+
error: named argument never used
98+
--> $DIR/ifmt-bad-arg.rs:45:26
99+
|
100+
LL | format!("{}", 1, foo=2); //~ ERROR: named argument never used
101+
| ^
102+
103+
error: argument never used
104+
--> $DIR/ifmt-bad-arg.rs:46:22
105+
|
106+
LL | format!("{foo}", 1, foo=2); //~ ERROR: argument never used
107+
| ^
108+
109+
error: named argument never used
110+
--> $DIR/ifmt-bad-arg.rs:47:21
111+
|
112+
LL | format!("", foo=2); //~ ERROR: named argument never used
113+
| ^
114+
115+
error: multiple unused formatting arguments
116+
--> $DIR/ifmt-bad-arg.rs:48:32
117+
|
118+
LL | format!("{} {}", 1, 2, foo=1, bar=2); //~ ERROR: multiple unused formatting arguments
119+
| ------- ^ ^
120+
| |
121+
| multiple missing formatting arguments
122+
123+
error: duplicate argument named `foo`
124+
--> $DIR/ifmt-bad-arg.rs:50:33
125+
|
126+
LL | format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument
127+
| ^
128+
|
129+
note: previously here
130+
--> $DIR/ifmt-bad-arg.rs:50:26
131+
|
132+
LL | format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument
133+
| ^
134+
135+
error: expected ident, positional arguments cannot follow named arguments
136+
--> $DIR/ifmt-bad-arg.rs:51:24
137+
|
138+
LL | format!("", foo=1, 2); //~ ERROR: positional arguments cannot follow
139+
| ^
140+
141+
error: there is no argument named `valueb`
142+
--> $DIR/ifmt-bad-arg.rs:55:13
143+
|
144+
LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
145+
| ^^^^^^^^^^^^^^^^^^^
146+
147+
error: named argument never used
148+
--> $DIR/ifmt-bad-arg.rs:55:51
149+
|
150+
LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
151+
| ^
152+
153+
error: invalid format string: expected `'}'` but string was terminated
154+
--> $DIR/ifmt-bad-arg.rs:61:15
155+
|
156+
LL | format!("{"); //~ ERROR: expected `'}'` but string was terminated
157+
| ^ expected `'}'` in format string
158+
|
159+
= note: if you intended to print `{`, you can escape it using `{{`
160+
161+
error: invalid format string: unmatched `}` found
162+
--> $DIR/ifmt-bad-arg.rs:63:18
163+
|
164+
LL | format!("foo } bar"); //~ ERROR: unmatched `}` found
165+
| ^ unmatched `}` in format string
166+
|
167+
= note: if you intended to print `}`, you can escape it using `}}`
168+
169+
error: invalid format string: unmatched `}` found
170+
--> $DIR/ifmt-bad-arg.rs:64:18
171+
|
172+
LL | format!("foo }"); //~ ERROR: unmatched `}` found
173+
| ^ unmatched `}` in format string
174+
|
175+
= note: if you intended to print `}`, you can escape it using `}}`
176+
177+
error: argument never used
178+
--> $DIR/ifmt-bad-arg.rs:66:27
179+
|
180+
LL | format!("foo %s baz", "bar"); //~ ERROR: argument never used
181+
| ^^^^^
182+
|
183+
= help: `%s` should be written as `{}`
184+
= note: printf formatting not supported; see the documentation for `std::fmt`
185+
186+
error: aborting due to 26 previous errors
187+

src/test/ui/macros/macro-backtrace-println.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: 1 positional argument in format string, but no arguments were given
2-
--> $DIR/macro-backtrace-println.rs:24:30
2+
--> $DIR/macro-backtrace-println.rs:24:31
33
|
44
LL | ($fmt:expr) => (myprint!(concat!($fmt, "/n"))); //~ ERROR no arguments were given
5-
| ^^^^^^^^^^^^^^^^^^^
5+
| ^^
66
...
77
LL | myprintln!("{}");
88
| ----------------- in this macro invocation

0 commit comments

Comments
 (0)