On format!() arg count mismatch provide extra info
When positional width and precision formatting flags are present in a
formatting string that has an argument count mismatch, provide extra
information pointing at them making it easiser to understand where the
problem may lay:
```
error: 4 positional arguments in format string, but there are 3 arguments
--> $DIR/ifmt-bad-arg.rs:78:15
|
LL | println!("{} {:.*} {}", 1, 3.2, 4);
| ^^ ^^--^ ^^ --- this parameter corresponds to the precision flag
| |
| this precision flag adds an extra required argument at position 1, which is why there are 4 arguments expected
|
= note: positional arguments are zero-based
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
error: 4 positional arguments in format string, but there are 3 arguments
--> $DIR/ifmt-bad-arg.rs:81:15
|
LL | println!("{} {:07$.*} {}", 1, 3.2, 4);
| ^^ ^^-----^ ^^ --- this parameter corresponds to the precision flag
| | |
| | this precision flag adds an extra required argument at position 1, which is why there are 4 arguments expected
| this width flag expects an `usize` argument at position 7, but there are 3 arguments
|
= note: positional arguments are zero-based
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
error: 3 positional arguments in format string, but there are 3 arguments
--> $DIR/ifmt-bad-arg.rs:84:15
|
LL | println!("{} {:07$} {}", 1, 3.2, 4);
| ^^ ^^---^ ^^
| |
| this width flag expects an `usize` argument at position 7, but there are 3 arguments
|
= note: positional arguments are zero-based
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
```
This commit is contained in:
@@ -109,6 +109,8 @@ struct Context<'a, 'b> {
|
||||
invalid_refs: Vec<(usize, usize)>,
|
||||
/// Spans of all the formatting arguments, in order.
|
||||
arg_spans: Vec<Span>,
|
||||
/// All the formatting arguments that have formatting flags set, in order for diagnostics.
|
||||
arg_with_formatting: Vec<parse::FormatSpec<'a>>,
|
||||
/// Whether this formatting string is a literal or it comes from a macro.
|
||||
is_literal: bool,
|
||||
}
|
||||
@@ -279,14 +281,20 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
.iter()
|
||||
.map(|(r, pos)| (r.to_string(), self.arg_spans.get(*pos)));
|
||||
|
||||
let mut zero_based_note = false;
|
||||
|
||||
if self.names.is_empty() && !numbered_position_args {
|
||||
let count = self.pieces.len() + self.arg_with_formatting
|
||||
.iter()
|
||||
.filter(|fmt| fmt.precision_span.is_some())
|
||||
.count();
|
||||
e = self.ecx.mut_span_err(
|
||||
sp,
|
||||
&format!(
|
||||
"{} positional argument{} in format string, but {}",
|
||||
self.pieces.len(),
|
||||
if self.pieces.len() > 1 { "s" } else { "" },
|
||||
self.describe_num_args()
|
||||
count,
|
||||
if count > 1 { "s" } else { "" },
|
||||
self.describe_num_args(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
@@ -317,9 +325,70 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
&format!("invalid reference to positional {} ({})",
|
||||
arg_list,
|
||||
self.describe_num_args()));
|
||||
e.note("positional arguments are zero-based");
|
||||
zero_based_note = true;
|
||||
};
|
||||
|
||||
for fmt in &self.arg_with_formatting {
|
||||
if let Some(span) = fmt.precision_span {
|
||||
let span = self.fmtsp.from_inner(span);
|
||||
match fmt.precision {
|
||||
parse::CountIsParam(pos) if pos > self.args.len() => {
|
||||
e.span_label(span, &format!(
|
||||
"this precision flag expects an `usize` argument at position {}, \
|
||||
but {}",
|
||||
pos,
|
||||
self.describe_num_args(),
|
||||
));
|
||||
zero_based_note = true;
|
||||
}
|
||||
parse::CountIsParam(pos) => {
|
||||
let count = self.pieces.len() + self.arg_with_formatting
|
||||
.iter()
|
||||
.filter(|fmt| fmt.precision_span.is_some())
|
||||
.count();
|
||||
e.span_label(span, &format!(
|
||||
"this precision flag adds an extra required argument at position {}, \
|
||||
which is why there {} expected",
|
||||
pos,
|
||||
if count == 1 {
|
||||
"is 1 argument".to_string()
|
||||
} else {
|
||||
format!("are {} arguments", count)
|
||||
},
|
||||
));
|
||||
e.span_label(
|
||||
self.args[pos].span,
|
||||
"this parameter corresponds to the precision flag",
|
||||
);
|
||||
zero_based_note = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(span) = fmt.width_span {
|
||||
let span = self.fmtsp.from_inner(span);
|
||||
match fmt.width {
|
||||
parse::CountIsParam(pos) if pos > self.args.len() => {
|
||||
e.span_label(span, &format!(
|
||||
"this width flag expects an `usize` argument at position {}, \
|
||||
but {}",
|
||||
pos,
|
||||
self.describe_num_args(),
|
||||
));
|
||||
zero_based_note = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if zero_based_note {
|
||||
e.note("positional arguments are zero-based");
|
||||
}
|
||||
if !self.arg_with_formatting.is_empty() {
|
||||
e.note("for information about formatting flags, visit \
|
||||
https://doc.rust-lang.org/std/fmt/index.html");
|
||||
}
|
||||
|
||||
e.emit();
|
||||
}
|
||||
|
||||
@@ -435,10 +504,11 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
|
||||
/// Builds a static `rt::Argument` from a `parse::Piece` or append
|
||||
/// to the `literal` string.
|
||||
fn build_piece(&mut self,
|
||||
piece: &parse::Piece<'_>,
|
||||
arg_index_consumed: &mut Vec<usize>)
|
||||
-> Option<P<ast::Expr>> {
|
||||
fn build_piece(
|
||||
&mut self,
|
||||
piece: &parse::Piece<'a>,
|
||||
arg_index_consumed: &mut Vec<usize>,
|
||||
) -> Option<P<ast::Expr>> {
|
||||
let sp = self.macsp;
|
||||
match *piece {
|
||||
parse::String(s) => {
|
||||
@@ -496,7 +566,9 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
align: parse::AlignUnknown,
|
||||
flags: 0,
|
||||
precision: parse::CountImplied,
|
||||
precision_span: None,
|
||||
width: parse::CountImplied,
|
||||
width_span: None,
|
||||
ty: arg.format.ty,
|
||||
},
|
||||
};
|
||||
@@ -506,6 +578,9 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
let pos_simple =
|
||||
arg.position.index() == simple_arg.position.index();
|
||||
|
||||
if arg.format.precision_span.is_some() || arg.format.width_span.is_some() {
|
||||
self.arg_with_formatting.push(arg.format); //'liself.fmtsp.from_inner(span));
|
||||
}
|
||||
if !pos_simple || arg.format != simple_arg.format || fill != ' ' {
|
||||
self.all_pieces_simple = false;
|
||||
}
|
||||
@@ -530,7 +605,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
|
||||
let fmt = self.ecx.expr_struct(
|
||||
sp,
|
||||
path,
|
||||
path,
|
||||
vec![
|
||||
self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
|
||||
self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
|
||||
@@ -657,12 +732,13 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
self.ecx.expr_call_global(self.macsp, path, fn_args)
|
||||
}
|
||||
|
||||
fn format_arg(ecx: &ExtCtxt<'_>,
|
||||
macsp: Span,
|
||||
mut sp: Span,
|
||||
ty: &ArgumentType,
|
||||
arg: ast::Ident)
|
||||
-> P<ast::Expr> {
|
||||
fn format_arg(
|
||||
ecx: &ExtCtxt<'_>,
|
||||
macsp: Span,
|
||||
mut sp: Span,
|
||||
ty: &ArgumentType,
|
||||
arg: ast::Ident,
|
||||
) -> P<ast::Expr> {
|
||||
sp = sp.apply_mark(ecx.current_expansion.id);
|
||||
let arg = ecx.expr_ident(sp, arg);
|
||||
let trait_ = match *ty {
|
||||
@@ -941,6 +1017,7 @@ pub fn expand_preparsed_format_args(
|
||||
fmtsp: fmt.span,
|
||||
invalid_refs: Vec::new(),
|
||||
arg_spans,
|
||||
arg_with_formatting: Vec::new(),
|
||||
is_literal,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user