Rollup merge of #52649 - estebank:fmt-span, r=oli-obk
Point spans to inner elements of format strings - Point at missing positional specifiers in string literal ``` error: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments) --> $DIR/ifmt-bad-arg.rs:34:38 | LL | format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2); | ^^ ^^ ^^ | = note: positional arguments are zero-based ``` - Point at named formatting specifier in string literal ``` error: there is no argument named `foo` --> $DIR/ifmt-bad-arg.rs:37:17 | LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); | ^^^^^ ``` - Update label for formatting string in "multiple unused formatting arguments" to be more correct ``` error: multiple unused formatting arguments --> $DIR/ifmt-bad-arg.rs:42:17 | LL | format!("", 1, 2); //~ ERROR: multiple unused formatting arguments | -- ^ ^ | | | multiple missing formatting specifiers ``` - When using `printf` string formatting, provide a structured suggestion instead of a note ``` error: multiple unused formatting arguments --> $DIR/format-foreign.rs:12:30 | LL | println!("%.*3$s %s!/n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments | -------------- ^^^^^^^^ ^^^^^^^ ^ | | | 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!("{:.2$} {}!/n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments | ^^^^^^ ^^ ```
This commit is contained in:
commit
2aec4e882c
8 changed files with 494 additions and 116 deletions
|
@ -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,7 @@ pub mod printf {
|
|||
_ => {/* fall-through */},
|
||||
}
|
||||
|
||||
Cur::new_at_start(&s[start..])
|
||||
Cur::new_at(&s[..], start)
|
||||
};
|
||||
|
||||
// This is meant to be a translation of the following regex:
|
||||
|
@ -355,6 +383,7 @@ pub mod printf {
|
|||
precision: None,
|
||||
length: None,
|
||||
type_: at.slice_between(next).unwrap(),
|
||||
position: (start.at, next.at),
|
||||
}),
|
||||
next.slice_after()
|
||||
));
|
||||
|
@ -541,6 +570,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 +580,7 @@ pub mod printf {
|
|||
precision,
|
||||
length,
|
||||
type_,
|
||||
position,
|
||||
};
|
||||
Some((Substitution::Format(f), end.slice_after()))
|
||||
}
|
||||
|
@ -616,6 +647,7 @@ pub mod printf {
|
|||
($in_:expr, {
|
||||
$param:expr, $flags:expr,
|
||||
$width:expr, $prec:expr, $len:expr, $type_:expr,
|
||||
$pos:expr,
|
||||
}) => {
|
||||
assert_eq!(
|
||||
pns(concat!($in_, "!")),
|
||||
|
@ -628,6 +660,7 @@ pub mod printf {
|
|||
precision: $prec,
|
||||
length: $len,
|
||||
type_: $type_,
|
||||
position: $pos,
|
||||
}),
|
||||
"!"
|
||||
))
|
||||
|
@ -636,53 +669,53 @@ pub mod printf {
|
|||
}
|
||||
|
||||
assert_pns_eq_sub!("%!",
|
||||
{ None, "", None, None, None, "!", });
|
||||
{ None, "", None, None, None, "!", (0, 2), });
|
||||
assert_pns_eq_sub!("%c",
|
||||
{ None, "", None, None, None, "c", });
|
||||
{ None, "", None, None, None, "c", (0, 2), });
|
||||
assert_pns_eq_sub!("%s",
|
||||
{ None, "", None, None, None, "s", });
|
||||
{ None, "", None, None, None, "s", (0, 2), });
|
||||
assert_pns_eq_sub!("%06d",
|
||||
{ None, "0", Some(N::Num(6)), None, None, "d", });
|
||||
{ None, "0", Some(N::Num(6)), None, None, "d", (0, 4), });
|
||||
assert_pns_eq_sub!("%4.2f",
|
||||
{ None, "", Some(N::Num(4)), Some(N::Num(2)), None, "f", });
|
||||
{ None, "", Some(N::Num(4)), Some(N::Num(2)), None, "f", (0, 5), });
|
||||
assert_pns_eq_sub!("%#x",
|
||||
{ None, "#", None, None, None, "x", });
|
||||
{ None, "#", None, None, None, "x", (0, 3), });
|
||||
assert_pns_eq_sub!("%-10s",
|
||||
{ None, "-", Some(N::Num(10)), None, None, "s", });
|
||||
{ None, "-", Some(N::Num(10)), None, None, "s", (0, 5), });
|
||||
assert_pns_eq_sub!("%*s",
|
||||
{ None, "", Some(N::Next), None, None, "s", });
|
||||
{ None, "", Some(N::Next), None, None, "s", (0, 3), });
|
||||
assert_pns_eq_sub!("%-10.*s",
|
||||
{ None, "-", Some(N::Num(10)), Some(N::Next), None, "s", });
|
||||
{ None, "-", Some(N::Num(10)), Some(N::Next), None, "s", (0, 7), });
|
||||
assert_pns_eq_sub!("%-*.*s",
|
||||
{ None, "-", Some(N::Next), Some(N::Next), None, "s", });
|
||||
{ None, "-", Some(N::Next), Some(N::Next), None, "s", (0, 6), });
|
||||
assert_pns_eq_sub!("%.6i",
|
||||
{ None, "", None, Some(N::Num(6)), None, "i", });
|
||||
{ None, "", None, Some(N::Num(6)), None, "i", (0, 4), });
|
||||
assert_pns_eq_sub!("%+i",
|
||||
{ None, "+", None, None, None, "i", });
|
||||
{ None, "+", None, None, None, "i", (0, 3), });
|
||||
assert_pns_eq_sub!("%08X",
|
||||
{ None, "0", Some(N::Num(8)), None, None, "X", });
|
||||
{ None, "0", Some(N::Num(8)), None, None, "X", (0, 4), });
|
||||
assert_pns_eq_sub!("%lu",
|
||||
{ None, "", None, None, Some("l"), "u", });
|
||||
{ None, "", None, None, Some("l"), "u", (0, 3), });
|
||||
assert_pns_eq_sub!("%Iu",
|
||||
{ None, "", None, None, Some("I"), "u", });
|
||||
{ None, "", None, None, Some("I"), "u", (0, 3), });
|
||||
assert_pns_eq_sub!("%I32u",
|
||||
{ None, "", None, None, Some("I32"), "u", });
|
||||
{ None, "", None, None, Some("I32"), "u", (0, 5), });
|
||||
assert_pns_eq_sub!("%I64u",
|
||||
{ None, "", None, None, Some("I64"), "u", });
|
||||
{ None, "", None, None, Some("I64"), "u", (0, 5), });
|
||||
assert_pns_eq_sub!("%'d",
|
||||
{ None, "'", None, None, None, "d", });
|
||||
{ None, "'", None, None, None, "d", (0, 3), });
|
||||
assert_pns_eq_sub!("%10s",
|
||||
{ None, "", Some(N::Num(10)), None, None, "s", });
|
||||
{ None, "", Some(N::Num(10)), None, None, "s", (0, 4), });
|
||||
assert_pns_eq_sub!("%-10.10s",
|
||||
{ None, "-", Some(N::Num(10)), Some(N::Num(10)), None, "s", });
|
||||
{ None, "-", Some(N::Num(10)), Some(N::Num(10)), None, "s", (0, 8), });
|
||||
assert_pns_eq_sub!("%1$d",
|
||||
{ Some(1), "", None, None, None, "d", });
|
||||
{ Some(1), "", None, None, None, "d", (0, 4), });
|
||||
assert_pns_eq_sub!("%2$.*3$d",
|
||||
{ Some(2), "", None, Some(N::Arg(3)), None, "d", });
|
||||
{ Some(2), "", None, Some(N::Arg(3)), None, "d", (0, 8), });
|
||||
assert_pns_eq_sub!("%1$*2$.*3$d",
|
||||
{ Some(1), "", Some(N::Arg(2)), Some(N::Arg(3)), None, "d", });
|
||||
{ Some(1), "", Some(N::Arg(2)), Some(N::Arg(3)), None, "d", (0, 11), });
|
||||
assert_pns_eq_sub!("%-8ld",
|
||||
{ None, "-", Some(N::Num(8)), None, Some("l"), "d", });
|
||||
{ None, "-", Some(N::Num(8)), None, Some("l"), "d", (0, 5), });
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -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(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),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue