Skip to content

Commit 4d8aa59

Browse files
committed
Use suggestions for printf format
1 parent f9e3762 commit 4d8aa59

File tree

5 files changed

+102
-14
lines changed

5 files changed

+102
-14
lines changed

src/libsyntax_ext/format.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
948948
($kind:ident) => {{
949949
let mut show_doc_note = false;
950950

951+
let mut suggestions = vec![];
951952
for sub in foreign::$kind::iter_subs(fmt_str) {
952953
let trn = match sub.translate() {
953954
Some(trn) => trn,
@@ -956,6 +957,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
956957
None => continue,
957958
};
958959

960+
let pos = sub.position();
959961
let sub = String::from(sub.as_str());
960962
if explained.contains(&sub) {
961963
continue;
@@ -967,7 +969,14 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
967969
show_doc_note = true;
968970
}
969971

970-
diag.help(&format!("`{}` should be written as `{}`", sub, trn));
972+
if let Some((start, end)) = pos {
973+
// account for `"` and account for raw strings `r#`
974+
let padding = str_style.map(|i| i + 2).unwrap_or(1);
975+
let sp = fmt_sp.from_inner_byte_pos(start + padding, end + padding);
976+
suggestions.push((sp, trn));
977+
} else {
978+
diag.help(&format!("`{}` should be written as `{}`", sub, trn));
979+
}
971980
}
972981

973982
if show_doc_note {
@@ -976,6 +985,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
976985
" formatting not supported; see the documentation for `std::fmt`",
977986
));
978987
}
988+
if suggestions.len() > 0 {
989+
diag.multipart_suggestion(
990+
"format specifiers in Rust are written using `{}`",
991+
suggestions,
992+
);
993+
}
979994
}};
980995
}
981996

src/libsyntax_ext/format_foreign.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub mod printf {
1414
/// Represents a single `printf`-style substitution.
1515
#[derive(Clone, PartialEq, Debug)]
1616
pub enum Substitution<'a> {
17-
/// A formatted output substitution.
17+
/// A formatted output substitution with its internal byte offset.
1818
Format(Format<'a>),
1919
/// A literal `%%` escape.
2020
Escape,
@@ -28,6 +28,23 @@ pub mod printf {
2828
}
2929
}
3030

31+
pub fn position(&self) -> Option<(usize, usize)> {
32+
match *self {
33+
Substitution::Format(ref fmt) => Some(fmt.position),
34+
_ => None,
35+
}
36+
}
37+
38+
pub fn set_position(&mut self, start: usize, end: usize) {
39+
match self {
40+
Substitution::Format(ref mut fmt) => {
41+
fmt.position = (start, end);
42+
}
43+
_ => {}
44+
}
45+
}
46+
47+
3148
/// Translate this substitution into an equivalent Rust formatting directive.
3249
///
3350
/// This ignores cases where the substitution does not have an exact equivalent, or where
@@ -57,6 +74,8 @@ pub mod printf {
5774
pub length: Option<&'a str>,
5875
/// Type of parameter being converted.
5976
pub type_: &'a str,
77+
/// Byte offset for the start and end of this formatting directive.
78+
pub position: (usize, usize),
6079
}
6180

6281
impl<'a> Format<'a> {
@@ -257,19 +276,28 @@ pub mod printf {
257276
pub fn iter_subs(s: &str) -> Substitutions {
258277
Substitutions {
259278
s,
279+
pos: 0,
260280
}
261281
}
262282

263283
/// Iterator over substitutions in a string.
264284
pub struct Substitutions<'a> {
265285
s: &'a str,
286+
pos: usize,
266287
}
267288

268289
impl<'a> Iterator for Substitutions<'a> {
269290
type Item = Substitution<'a>;
270291
fn next(&mut self) -> Option<Self::Item> {
271-
let (sub, tail) = parse_next_substitution(self.s)?;
292+
let (mut sub, tail) = parse_next_substitution(self.s)?;
272293
self.s = tail;
294+
match sub {
295+
Substitution::Format(_) => if let Some((start, end)) = sub.position() {
296+
sub.set_position(start + self.pos, end + self.pos);
297+
self.pos += end;
298+
}
299+
Substitution::Escape => self.pos += 2,
300+
}
273301
Some(sub)
274302
}
275303

@@ -301,7 +329,9 @@ pub mod printf {
301329
_ => {/* fall-through */},
302330
}
303331

304-
Cur::new_at_start(&s[start..])
332+
//let _ = Cur::new_at_start_with_pos(&s[..], start);
333+
//Cur::new_at_start(&s[start..])
334+
Cur::new_at_start_with_pos(&s[..], start)
305335
};
306336

307337
// This is meant to be a translation of the following regex:
@@ -355,6 +385,7 @@ pub mod printf {
355385
precision: None,
356386
length: None,
357387
type_: at.slice_between(next).unwrap(),
388+
position: (start.at, next.at),
358389
}),
359390
next.slice_after()
360391
));
@@ -541,6 +572,7 @@ pub mod printf {
541572
drop(next);
542573

543574
end = at;
575+
let position = (start.at, end.at);
544576

545577
let f = Format {
546578
span: start.slice_between(end).unwrap(),
@@ -550,6 +582,7 @@ pub mod printf {
550582
precision,
551583
length,
552584
type_,
585+
position,
553586
};
554587
Some((Substitution::Format(f), end.slice_after()))
555588
}
@@ -755,6 +788,12 @@ pub mod shell {
755788
}
756789
}
757790

791+
pub fn position(&self) -> Option<(usize, usize)> {
792+
match *self {
793+
_ => None,
794+
}
795+
}
796+
758797
pub fn translate(&self) -> Option<String> {
759798
match *self {
760799
Substitution::Ordinal(n) => Some(format!("{{{}}}", n)),
@@ -918,7 +957,7 @@ mod strcursor {
918957

919958
pub struct StrCursor<'a> {
920959
s: &'a str,
921-
at: usize,
960+
pub at: usize,
922961
}
923962

924963
impl<'a> StrCursor<'a> {
@@ -929,6 +968,13 @@ mod strcursor {
929968
}
930969
}
931970

971+
pub fn new_at_start_with_pos(s: &'a str, at: usize) -> StrCursor<'a> {
972+
StrCursor {
973+
s,
974+
at,
975+
}
976+
}
977+
932978
pub fn at_next_cp(mut self) -> Option<StrCursor<'a>> {
933979
match self.try_seek_right_cp() {
934980
true => Some(self),

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,10 @@ error: argument never used
178178
--> $DIR/ifmt-bad-arg.rs:66:27
179179
|
180180
LL | format!("foo %s baz", "bar"); //~ ERROR: argument never used
181-
| ^^^^^
181+
| -- ^^^^^
182+
| |
183+
| help: format specifiers in Rust are written using `{}`: `{}`
182184
|
183-
= help: `%s` should be written as `{}`
184185
= note: printf formatting not supported; see the documentation for `std::fmt`
185186

186187
error: there is no argument named `foo`

src/test/ui/macros/format-foreign.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
fn main() {
1212
println!("%.*3$s %s!\n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments
1313
println!("%1$*2$.*3$f", 123.456); //~ ERROR never used
14+
println!(r###"%.*3$s
15+
%s!\n
16+
"###, "Hello,", "World", 4);
17+
//~^ ERROR multiple unused formatting arguments
18+
// correctly account for raw strings in inline suggestions
1419

1520
// This should *not* produce hints, on the basis that there's equally as
1621
// many "correct" format specifiers. It's *probably* just an actual typo.

src/test/ui/macros/format-foreign.stderr

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,54 @@ LL | println!("%.*3$s %s!/n", "Hello,", "World", 4); //~ ERROR multiple unus
66
| |
77
| multiple missing formatting specifiers
88
|
9-
= help: `%.*3$s` should be written as `{:.2$}`
10-
= help: `%s` should be written as `{}`
119
= note: printf formatting not supported; see the documentation for `std::fmt`
10+
help: format specifiers in Rust are written using `{}`
11+
|
12+
LL | println!("{:.2$} {}!/n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments
13+
| ^^^^^^ ^^
1214

1315
error: argument never used
1416
--> $DIR/format-foreign.rs:13:29
1517
|
1618
LL | println!("%1$*2$.*3$f", 123.456); //~ ERROR never used
17-
| ^^^^^^^
19+
| ----------- ^^^^^^^
20+
| |
21+
| help: format specifiers in Rust are written using `{}`: `{0:1$.2$}`
22+
|
23+
= note: printf formatting not supported; see the documentation for `std::fmt`
24+
25+
error: multiple unused formatting arguments
26+
--> $DIR/format-foreign.rs:16:7
27+
|
28+
LL | println!(r###"%.*3$s
29+
| ______________-
30+
LL | | %s!/n
31+
LL | | "###, "Hello,", "World", 4);
32+
| | - ^^^^^^^^ ^^^^^^^ ^
33+
| |____|
34+
| multiple missing formatting specifiers
1835
|
19-
= help: `%1$*2$.*3$f` should be written as `{0:1$.2$}`
2036
= note: printf formatting not supported; see the documentation for `std::fmt`
37+
help: format specifiers in Rust are written using `{}`
38+
|
39+
LL | println!(r###"{:.2$}
40+
LL | {}!/n
41+
|
2142

2243
error: argument never used
23-
--> $DIR/format-foreign.rs:17:30
44+
--> $DIR/format-foreign.rs:22:30
2445
|
2546
LL | println!("{} %f", "one", 2.0); //~ ERROR never used
2647
| ^^^
2748

2849
error: named argument never used
29-
--> $DIR/format-foreign.rs:19:39
50+
--> $DIR/format-foreign.rs:24:39
3051
|
3152
LL | println!("Hi there, $NAME.", NAME="Tim"); //~ ERROR never used
3253
| ^^^^^
3354
|
3455
= help: `$NAME` should be written as `{NAME}`
3556
= note: shell formatting not supported; see the documentation for `std::fmt`
3657

37-
error: aborting due to 4 previous errors
58+
error: aborting due to 5 previous errors
3859

0 commit comments

Comments
 (0)