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) => {{
let mut show_doc_note = false;
let mut suggestions = vec![];
for sub in foreign::$kind::iter_subs(fmt_str) {
let trn = match sub.translate() {
Some(trn) => trn,
@ -956,6 +957,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
None => continue,
};
let pos = sub.position();
let sub = String::from(sub.as_str());
if explained.contains(&sub) {
continue;
@ -967,7 +969,14 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
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 {
@ -976,6 +985,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
" 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.
#[derive(Clone, PartialEq, Debug)]
pub enum Substitution<'a> {
/// A formatted output substitution.
/// A formatted output substitution with its internal byte offset.
Format(Format<'a>),
/// A literal `%%` 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.
///
/// 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>,
/// Type of parameter being converted.
pub type_: &'a str,
/// Byte offset for the start and end of this formatting directive.
pub position: (usize, usize),
}
impl<'a> Format<'a> {
@ -257,19 +276,28 @@ pub mod printf {
pub fn iter_subs(s: &str) -> Substitutions {
Substitutions {
s,
pos: 0,
}
}
/// Iterator over substitutions in a string.
pub struct Substitutions<'a> {
s: &'a str,
pos: usize,
}
impl<'a> Iterator for Substitutions<'a> {
type Item = Substitution<'a>;
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;
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)
}
@ -301,7 +329,9 @@ pub mod printf {
_ => {/* 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:
@ -355,6 +385,7 @@ pub mod printf {
precision: None,
length: None,
type_: at.slice_between(next).unwrap(),
position: (start.at, next.at),
}),
next.slice_after()
));
@ -541,6 +572,7 @@ pub mod printf {
drop(next);
end = at;
let position = (start.at, end.at);
let f = Format {
span: start.slice_between(end).unwrap(),
@ -550,6 +582,7 @@ pub mod printf {
precision,
length,
type_,
position,
};
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> {
match *self {
Substitution::Ordinal(n) => Some(format!("{{{}}}", n)),
@ -918,7 +957,7 @@ mod strcursor {
pub struct StrCursor<'a> {
s: &'a str,
at: usize,
pub at: usize,
}
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>> {
match self.try_seek_right_cp() {
true => Some(self),

View file

@ -178,9 +178,10 @@ error: argument never used
--> $DIR/ifmt-bad-arg.rs:66:27
|
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`
error: there is no argument named `foo`

View file

@ -11,6 +11,11 @@
fn main() {
println!("%.*3$s %s!\n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments
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
// 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
|
= 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`
help: format specifiers in Rust are written using `{}`
|
LL | println!("{:.2$} {}!/n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments
| ^^^^^^ ^^
error: argument never used
--> $DIR/format-foreign.rs:13:29
|
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`
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
--> $DIR/format-foreign.rs:17:30
--> $DIR/format-foreign.rs:22:30
|
LL | println!("{} %f", "one", 2.0); //~ ERROR 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
| ^^^^^
@ -34,5 +55,5 @@ LL | println!("Hi there, $NAME.", NAME="Tim"); //~ ERROR never used
= help: `$NAME` should be written as `{NAME}`
= 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