1
Fork 0

Use suggestions for printf format

This commit is contained in:
Esteban Küber 2018-07-24 16:01:38 -07:00
parent f9e37625e6
commit 4d8aa5989c
5 changed files with 102 additions and 14 deletions

View file

@ -948,6 +948,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
($kind:ident) => {{ ($kind:ident) => {{
let mut show_doc_note = false; let mut show_doc_note = false;
let mut suggestions = vec![];
for sub in foreign::$kind::iter_subs(fmt_str) { for sub in foreign::$kind::iter_subs(fmt_str) {
let trn = match sub.translate() { let trn = match sub.translate() {
Some(trn) => trn, Some(trn) => trn,
@ -956,6 +957,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
None => continue, None => continue,
}; };
let pos = sub.position();
let sub = String::from(sub.as_str()); let sub = String::from(sub.as_str());
if explained.contains(&sub) { if explained.contains(&sub) {
continue; continue;
@ -967,7 +969,14 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
show_doc_note = true; show_doc_note = true;
} }
diag.help(&format!("`{}` should be written as `{}`", sub, trn)); if let Some((start, end)) = pos {
// account for `"` and account for raw strings `r#`
let padding = str_style.map(|i| i + 2).unwrap_or(1);
let sp = fmt_sp.from_inner_byte_pos(start + padding, end + padding);
suggestions.push((sp, trn));
} else {
diag.help(&format!("`{}` should be written as `{}`", sub, trn));
}
} }
if show_doc_note { if show_doc_note {
@ -976,6 +985,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
" formatting not supported; see the documentation for `std::fmt`", " formatting not supported; see the documentation for `std::fmt`",
)); ));
} }
if suggestions.len() > 0 {
diag.multipart_suggestion(
"format specifiers in Rust are written using `{}`",
suggestions,
);
}
}}; }};
} }

View file

@ -14,7 +14,7 @@ pub mod printf {
/// Represents a single `printf`-style substitution. /// Represents a single `printf`-style substitution.
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum Substitution<'a> { pub enum Substitution<'a> {
/// A formatted output substitution. /// A formatted output substitution with its internal byte offset.
Format(Format<'a>), Format(Format<'a>),
/// A literal `%%` escape. /// A literal `%%` escape.
Escape, Escape,
@ -28,6 +28,23 @@ pub mod printf {
} }
} }
pub fn position(&self) -> Option<(usize, usize)> {
match *self {
Substitution::Format(ref fmt) => Some(fmt.position),
_ => None,
}
}
pub fn set_position(&mut self, start: usize, end: usize) {
match self {
Substitution::Format(ref mut fmt) => {
fmt.position = (start, end);
}
_ => {}
}
}
/// Translate this substitution into an equivalent Rust formatting directive. /// Translate this substitution into an equivalent Rust formatting directive.
/// ///
/// This ignores cases where the substitution does not have an exact equivalent, or where /// This ignores cases where the substitution does not have an exact equivalent, or where
@ -57,6 +74,8 @@ pub mod printf {
pub length: Option<&'a str>, pub length: Option<&'a str>,
/// Type of parameter being converted. /// Type of parameter being converted.
pub type_: &'a str, pub type_: &'a str,
/// Byte offset for the start and end of this formatting directive.
pub position: (usize, usize),
} }
impl<'a> Format<'a> { impl<'a> Format<'a> {
@ -257,19 +276,28 @@ pub mod printf {
pub fn iter_subs(s: &str) -> Substitutions { pub fn iter_subs(s: &str) -> Substitutions {
Substitutions { Substitutions {
s, s,
pos: 0,
} }
} }
/// Iterator over substitutions in a string. /// Iterator over substitutions in a string.
pub struct Substitutions<'a> { pub struct Substitutions<'a> {
s: &'a str, s: &'a str,
pos: usize,
} }
impl<'a> Iterator for Substitutions<'a> { impl<'a> Iterator for Substitutions<'a> {
type Item = Substitution<'a>; type Item = Substitution<'a>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let (sub, tail) = parse_next_substitution(self.s)?; let (mut sub, tail) = parse_next_substitution(self.s)?;
self.s = tail; self.s = tail;
match sub {
Substitution::Format(_) => if let Some((start, end)) = sub.position() {
sub.set_position(start + self.pos, end + self.pos);
self.pos += end;
}
Substitution::Escape => self.pos += 2,
}
Some(sub) Some(sub)
} }
@ -301,7 +329,9 @@ pub mod printf {
_ => {/* fall-through */}, _ => {/* fall-through */},
} }
Cur::new_at_start(&s[start..]) //let _ = Cur::new_at_start_with_pos(&s[..], start);
//Cur::new_at_start(&s[start..])
Cur::new_at_start_with_pos(&s[..], start)
}; };
// This is meant to be a translation of the following regex: // This is meant to be a translation of the following regex:
@ -355,6 +385,7 @@ pub mod printf {
precision: None, precision: None,
length: None, length: None,
type_: at.slice_between(next).unwrap(), type_: at.slice_between(next).unwrap(),
position: (start.at, next.at),
}), }),
next.slice_after() next.slice_after()
)); ));
@ -541,6 +572,7 @@ pub mod printf {
drop(next); drop(next);
end = at; end = at;
let position = (start.at, end.at);
let f = Format { let f = Format {
span: start.slice_between(end).unwrap(), span: start.slice_between(end).unwrap(),
@ -550,6 +582,7 @@ pub mod printf {
precision, precision,
length, length,
type_, type_,
position,
}; };
Some((Substitution::Format(f), end.slice_after())) Some((Substitution::Format(f), end.slice_after()))
} }
@ -755,6 +788,12 @@ pub mod shell {
} }
} }
pub fn position(&self) -> Option<(usize, usize)> {
match *self {
_ => None,
}
}
pub fn translate(&self) -> Option<String> { pub fn translate(&self) -> Option<String> {
match *self { match *self {
Substitution::Ordinal(n) => Some(format!("{{{}}}", n)), Substitution::Ordinal(n) => Some(format!("{{{}}}", n)),
@ -918,7 +957,7 @@ mod strcursor {
pub struct StrCursor<'a> { pub struct StrCursor<'a> {
s: &'a str, s: &'a str,
at: usize, pub at: usize,
} }
impl<'a> StrCursor<'a> { impl<'a> StrCursor<'a> {
@ -929,6 +968,13 @@ mod strcursor {
} }
} }
pub fn new_at_start_with_pos(s: &'a str, at: usize) -> StrCursor<'a> {
StrCursor {
s,
at,
}
}
pub fn at_next_cp(mut self) -> Option<StrCursor<'a>> { pub fn at_next_cp(mut self) -> Option<StrCursor<'a>> {
match self.try_seek_right_cp() { match self.try_seek_right_cp() {
true => Some(self), true => Some(self),

View file

@ -178,9 +178,10 @@ error: argument never used
--> $DIR/ifmt-bad-arg.rs:66:27 --> $DIR/ifmt-bad-arg.rs:66:27
| |
LL | format!("foo %s baz", "bar"); //~ ERROR: argument never used LL | format!("foo %s baz", "bar"); //~ ERROR: argument never used
| ^^^^^ | -- ^^^^^
| |
| help: format specifiers in Rust are written using `{}`: `{}`
| |
= help: `%s` should be written as `{}`
= note: printf formatting not supported; see the documentation for `std::fmt` = note: printf formatting not supported; see the documentation for `std::fmt`
error: there is no argument named `foo` error: there is no argument named `foo`

View file

@ -11,6 +11,11 @@
fn main() { fn main() {
println!("%.*3$s %s!\n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments println!("%.*3$s %s!\n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments
println!("%1$*2$.*3$f", 123.456); //~ ERROR never used println!("%1$*2$.*3$f", 123.456); //~ ERROR never used
println!(r###"%.*3$s
%s!\n
"###, "Hello,", "World", 4);
//~^ ERROR multiple unused formatting arguments
// correctly account for raw strings in inline suggestions
// This should *not* produce hints, on the basis that there's equally as // This should *not* produce hints, on the basis that there's equally as
// many "correct" format specifiers. It's *probably* just an actual typo. // many "correct" format specifiers. It's *probably* just an actual typo.

View file

@ -6,27 +6,48 @@ LL | println!("%.*3$s %s!/n", "Hello,", "World", 4); //~ ERROR multiple unus
| | | |
| multiple missing formatting specifiers | multiple missing formatting specifiers
| |
= help: `%.*3$s` should be written as `{:.2$}`
= help: `%s` should be written as `{}`
= note: printf formatting not supported; see the documentation for `std::fmt` = note: printf formatting not supported; see the documentation for `std::fmt`
help: format specifiers in Rust are written using `{}`
|
LL | println!("{:.2$} {}!/n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments
| ^^^^^^ ^^
error: argument never used error: argument never used
--> $DIR/format-foreign.rs:13:29 --> $DIR/format-foreign.rs:13:29
| |
LL | println!("%1$*2$.*3$f", 123.456); //~ ERROR never used LL | println!("%1$*2$.*3$f", 123.456); //~ ERROR never used
| ^^^^^^^ | ----------- ^^^^^^^
| |
| help: format specifiers in Rust are written using `{}`: `{0:1$.2$}`
| |
= help: `%1$*2$.*3$f` should be written as `{0:1$.2$}`
= note: printf formatting not supported; see the documentation for `std::fmt` = note: printf formatting not supported; see the documentation for `std::fmt`
error: multiple unused formatting arguments
--> $DIR/format-foreign.rs:16:7
|
LL | println!(r###"%.*3$s
| ______________-
LL | | %s!/n
LL | | "###, "Hello,", "World", 4);
| | - ^^^^^^^^ ^^^^^^^ ^
| |____|
| multiple missing formatting specifiers
|
= note: printf formatting not supported; see the documentation for `std::fmt`
help: format specifiers in Rust are written using `{}`
|
LL | println!(r###"{:.2$}
LL | {}!/n
|
error: argument never used error: argument never used
--> $DIR/format-foreign.rs:17:30 --> $DIR/format-foreign.rs:22:30
| |
LL | println!("{} %f", "one", 2.0); //~ ERROR never used LL | println!("{} %f", "one", 2.0); //~ ERROR never used
| ^^^ | ^^^
error: named argument never used error: named argument never used
--> $DIR/format-foreign.rs:19:39 --> $DIR/format-foreign.rs:24:39
| |
LL | println!("Hi there, $NAME.", NAME="Tim"); //~ ERROR never used LL | println!("Hi there, $NAME.", NAME="Tim"); //~ ERROR never used
| ^^^^^ | ^^^^^
@ -34,5 +55,5 @@ LL | println!("Hi there, $NAME.", NAME="Tim"); //~ ERROR never used
= help: `$NAME` should be written as `{NAME}` = help: `$NAME` should be written as `{NAME}`
= note: shell formatting not supported; see the documentation for `std::fmt` = note: shell formatting not supported; see the documentation for `std::fmt`
error: aborting due to 4 previous errors error: aborting due to 5 previous errors