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) => {{
|
($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,8 +969,15 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
|
||||||
show_doc_note = true;
|
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));
|
diag.help(&format!("`{}` should be written as `{}`", sub, trn));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if show_doc_note {
|
if show_doc_note {
|
||||||
diag.note(concat!(
|
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`",
|
" 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.
|
/// 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),
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue