Skip to content

Commit 82d5ea4

Browse files
committed
Make format! positional argument errors clear
1 parent 74be072 commit 82d5ea4

File tree

1 file changed

+41
-8
lines changed

1 file changed

+41
-8
lines changed

src/libsyntax_ext/format.rs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ struct Context<'a, 'b: 'a> {
110110
/// still existed in this phase of processing.
111111
/// Used only for `all_pieces_simple` tracking in `trans_piece`.
112112
curarg: usize,
113+
/// Keep track of invalid references to positional arguments
114+
invalid_refs: Vec<usize>,
113115
}
114116

115117
/// Parses the arguments from the given list of tokens, returning None
@@ -251,23 +253,49 @@ impl<'a, 'b> Context<'a, 'b> {
251253

252254
fn describe_num_args(&self) -> String {
253255
match self.args.len() {
254-
0 => "no arguments given".to_string(),
255-
1 => "there is 1 argument".to_string(),
256-
x => format!("there are {} arguments", x),
256+
0 => "no arguments were given".to_string(),
257+
1 => "there is only 1 argument".to_string(),
258+
x => format!("there are only {} arguments", x),
257259
}
258260
}
259261

262+
/// Handle invalid references to positional arguments. Output different
263+
/// errors for the case where all arguments are positional and for when
264+
/// there are named arguments in the format string.
265+
fn report_invalid_references(&self) {
266+
let mut refs: Vec<String> = self.invalid_refs
267+
.iter()
268+
.map(|r| r.to_string())
269+
.collect();
270+
271+
let msg = if self.names.is_empty() {
272+
format!("{} positional argument{} in format string, but {}",
273+
self.pieces.len(),
274+
if self.pieces.len() > 1 { "s" } else { "" },
275+
self.describe_num_args())
276+
} else {
277+
let arg_list = match refs.len() {
278+
1 => format!("argument {}", refs.pop().unwrap()),
279+
_ => format!("arguments {head} and {tail}",
280+
tail=refs.pop().unwrap(),
281+
head=refs.join(", "))
282+
};
283+
284+
format!("invalid reference to positional {} ({})",
285+
arg_list,
286+
self.describe_num_args())
287+
};
288+
289+
self.ecx.span_err(self.fmtsp, &msg[..]);
290+
}
291+
260292
/// Actually verifies and tracks a given format placeholder
261293
/// (a.k.a. argument).
262294
fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
263295
match arg {
264296
Exact(arg) => {
265297
if self.args.len() <= arg {
266-
let msg = format!("invalid reference to argument `{}` ({})",
267-
arg,
268-
self.describe_num_args());
269-
270-
self.ecx.span_err(self.fmtsp, &msg[..]);
298+
self.invalid_refs.push(arg);
271299
return;
272300
}
273301
match ty {
@@ -691,6 +719,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
691719
all_pieces_simple: true,
692720
macsp,
693721
fmtsp: fmt.span,
722+
invalid_refs: Vec::new(),
694723
};
695724

696725
let fmt_str = &*fmt.node.0.as_str();
@@ -736,6 +765,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
736765
cx.str_pieces.push(s);
737766
}
738767

768+
if cx.invalid_refs.len() >= 1 {
769+
cx.report_invalid_references();
770+
}
771+
739772
// Make sure that all arguments were used and all arguments have types.
740773
let num_pos_args = cx.args.len() - cx.names.len();
741774
let mut errs = vec![];

0 commit comments

Comments
 (0)