Use suggestions for printf
format
This commit is contained in:
parent
f9e37625e6
commit
4d8aa5989c
5 changed files with 102 additions and 14 deletions
|
@ -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,8 +969,15 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
|
|||
show_doc_note = true;
|
||||
}
|
||||
|
||||
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 {
|
||||
diag.note(concat!(
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue