Skip to content

Commit 6bcf877

Browse files
committed
Point only at invalid positional arguments
1 parent 4230659 commit 6bcf877

File tree

2 files changed

+84
-70
lines changed

2 files changed

+84
-70
lines changed

src/libsyntax_ext/format.rs

Lines changed: 76 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ use self::Position::*;
1414
use fmt_macros as parse;
1515

1616
use syntax::ast;
17-
use syntax::ext::base::*;
1817
use syntax::ext::base;
18+
use syntax::ext::base::*;
1919
use syntax::ext::build::AstBuilder;
2020
use syntax::feature_gate;
2121
use syntax::parse::token;
2222
use syntax::ptr::P;
2323
use syntax::symbol::Symbol;
24-
use syntax_pos::{Span, MultiSpan, DUMMY_SP};
2524
use syntax::tokenstream;
25+
use syntax_pos::{MultiSpan, Span, DUMMY_SP};
2626

27-
use std::collections::{HashMap, HashSet};
2827
use std::collections::hash_map::Entry;
28+
use std::collections::{HashMap, HashSet};
2929

3030
#[derive(PartialEq)]
3131
enum ArgumentType {
@@ -111,9 +111,11 @@ struct Context<'a, 'b: 'a> {
111111
/// still existed in this phase of processing.
112112
/// Used only for `all_pieces_simple` tracking in `build_piece`.
113113
curarg: usize,
114+
/// Current piece being evaluated, used for error reporting.
114115
curpiece: usize,
115-
/// Keep track of invalid references to positional arguments
116-
invalid_refs: Vec<usize>,
116+
/// Keep track of invalid references to positional arguments.
117+
invalid_refs: Vec<(usize, usize)>,
118+
/// Spans of all the formatting arguments, in order.
117119
arg_spans: Vec<Span>,
118120
}
119121

@@ -157,15 +159,20 @@ fn parse_args(ecx: &mut ExtCtxt,
157159
i
158160
}
159161
_ if named => {
160-
ecx.span_err(p.span,
161-
"expected ident, positional arguments \
162-
cannot follow named arguments");
162+
ecx.span_err(
163+
p.span,
164+
"expected ident, positional arguments cannot follow named arguments",
165+
);
163166
return None;
164167
}
165168
_ => {
166-
ecx.span_err(p.span,
167-
&format!("expected ident for named argument, found `{}`",
168-
p.this_token_to_string()));
169+
ecx.span_err(
170+
p.span,
171+
&format!(
172+
"expected ident for named argument, found `{}`",
173+
p.this_token_to_string()
174+
),
175+
);
169176
return None;
170177
}
171178
};
@@ -267,34 +274,47 @@ impl<'a, 'b> Context<'a, 'b> {
267274
/// errors for the case where all arguments are positional and for when
268275
/// there are named arguments or numbered positional arguments in the
269276
/// format string.
270-
fn report_invalid_references(&self, numbered_position_args: bool, arg_places: &[(usize, usize)]) {
277+
fn report_invalid_references(&self, numbered_position_args: bool) {
271278
let mut e;
272-
let sps = arg_places.iter()
273-
.map(|&(start, end)| self.fmtsp.from_inner_byte_pos(start, end))
274-
.collect::<Vec<_>>();
275-
let sp = MultiSpan::from_spans(sps);
276-
let mut refs: Vec<_> = self.invalid_refs
279+
let sp = MultiSpan::from_spans(self.arg_spans.clone());
280+
let mut refs: Vec<_> = self
281+
.invalid_refs
277282
.iter()
278-
.map(|r| r.to_string())
283+
.map(|(r, pos)| (r.to_string(), self.arg_spans.get(*pos)))
279284
.collect();
280285

281286
if self.names.is_empty() && !numbered_position_args {
282-
e = self.ecx.mut_span_err(sp,
283-
&format!("{} positional argument{} in format string, but {}",
287+
e = self.ecx.mut_span_err(
288+
sp,
289+
&format!(
290+
"{} positional argument{} in format string, but {}",
284291
self.pieces.len(),
285292
if self.pieces.len() > 1 { "s" } else { "" },
286-
self.describe_num_args()));
293+
self.describe_num_args()
294+
),
295+
);
287296
} else {
288-
let arg_list = match refs.len() {
297+
let (arg_list, sp) = match refs.len() {
289298
1 => {
290-
let reg = refs.pop().unwrap();
291-
format!("argument {}", reg)
292-
},
299+
let (reg, pos) = refs.pop().unwrap();
300+
(
301+
format!("argument {}", reg),
302+
MultiSpan::from_span(*pos.unwrap_or(&self.fmtsp)),
303+
)
304+
}
293305
_ => {
306+
let pos =
307+
MultiSpan::from_spans(refs.iter().map(|(_, p)| *p.unwrap()).collect());
308+
let mut refs: Vec<String> = refs.iter().map(|(s, _)| s.to_owned()).collect();
294309
let reg = refs.pop().unwrap();
295-
format!("arguments {head} and {tail}",
296-
tail=reg,
297-
head=refs.join(", "))
310+
(
311+
format!(
312+
"arguments {head} and {tail}",
313+
tail = reg,
314+
head = refs.join(", ")
315+
),
316+
pos,
317+
)
298318
}
299319
};
300320

@@ -314,7 +334,7 @@ impl<'a, 'b> Context<'a, 'b> {
314334
match arg {
315335
Exact(arg) => {
316336
if self.args.len() <= arg {
317-
self.invalid_refs.push(arg);
337+
self.invalid_refs.push((arg, self.curpiece));
318338
return;
319339
}
320340
match ty {
@@ -520,33 +540,27 @@ impl<'a, 'b> Context<'a, 'b> {
520540
let prec = self.build_count(arg.format.precision);
521541
let width = self.build_count(arg.format.width);
522542
let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
523-
let fmt =
524-
self.ecx.expr_struct(sp,
543+
let fmt = self.ecx.expr_struct(
544+
sp,
525545
path,
526-
vec![self.ecx
527-
.field_imm(sp, self.ecx.ident_of("fill"), fill),
528-
self.ecx.field_imm(sp,
529-
self.ecx.ident_of("align"),
530-
align),
531-
self.ecx.field_imm(sp,
532-
self.ecx.ident_of("flags"),
533-
flags),
534-
self.ecx.field_imm(sp,
535-
self.ecx.ident_of("precision"),
536-
prec),
537-
self.ecx.field_imm(sp,
538-
self.ecx.ident_of("width"),
539-
width)]);
546+
vec![
547+
self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
548+
self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
549+
self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
550+
self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
551+
self.ecx.field_imm(sp, self.ecx.ident_of("width"), width),
552+
],
553+
);
540554

541555
let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument"));
542-
Some(self.ecx.expr_struct(sp,
556+
Some(self.ecx.expr_struct(
557+
sp,
543558
path,
544-
vec![self.ecx.field_imm(sp,
545-
self.ecx.ident_of("position"),
546-
pos),
547-
self.ecx.field_imm(sp,
548-
self.ecx.ident_of("format"),
549-
fmt)]))
559+
vec![
560+
self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
561+
self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt),
562+
],
563+
))
550564
}
551565
}
552566
}
@@ -559,9 +573,9 @@ impl<'a, 'b> Context<'a, 'b> {
559573
let mut pats = Vec::new();
560574
let mut heads = Vec::new();
561575

562-
let names_pos: Vec<_> = (0..self.args.len()).map(|i| {
563-
self.ecx.ident_of(&format!("arg{}", i)).gensym()
564-
}).collect();
576+
let names_pos: Vec<_> = (0..self.args.len())
577+
.map(|i| self.ecx.ident_of(&format!("arg{}", i)).gensym())
578+
.collect();
565579

566580
// First, build up the static array which will become our precompiled
567581
// format "string"
@@ -705,10 +719,11 @@ pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt,
705719
}
706720
}
707721

708-
pub fn expand_format_args_nl<'cx>(ecx: &'cx mut ExtCtxt,
722+
pub fn expand_format_args_nl<'cx>(
723+
ecx: &'cx mut ExtCtxt,
709724
mut sp: Span,
710-
tts: &[tokenstream::TokenTree])
711-
-> Box<dyn base::MacResult + 'cx> {
725+
tts: &[tokenstream::TokenTree],
726+
) -> Box<dyn base::MacResult + 'cx> {
712727
//if !ecx.ecfg.enable_allow_internal_unstable() {
713728

714729
// For some reason, the only one that actually works for `println` is the first check
@@ -759,7 +774,6 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
759774
let sugg_fmt = match args.len() {
760775
0 => "{}".to_string(),
761776
_ => format!("{}{{}}", "{} ".repeat(args.len())),
762-
763777
};
764778
err.span_suggestion(
765779
fmt_sp.shrink_to_lo(),
@@ -768,7 +782,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
768782
);
769783
err.emit();
770784
return DummyResult::raw_expr(sp);
771-
},
785+
}
772786
};
773787

774788
let mut cx = Context {
@@ -862,7 +876,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
862876
}
863877

864878
if cx.invalid_refs.len() >= 1 {
865-
cx.report_invalid_references(numbered_position_args, &parser.arg_places);
879+
cx.report_invalid_references(numbered_position_args);
866880
}
867881

868882
// Make sure that all arguments were used and all arguments have types.
@@ -894,7 +908,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
894908
} else {
895909
let mut diag = cx.ecx.struct_span_err(
896910
errs.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
897-
"multiple unused formatting arguments"
911+
"multiple unused formatting arguments",
898912
);
899913
diag.span_label(cx.fmtsp, "multiple missing formatting arguments");
900914
diag

src/test/ui/ifmt-bad-arg.stderr

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,34 +25,34 @@ LL | format!("{} {}");
2525
| ^^ ^^
2626

2727
error: invalid reference to positional argument 1 (there is 1 argument)
28-
--> $DIR/ifmt-bad-arg.rs:26:14
28+
--> $DIR/ifmt-bad-arg.rs:26:18
2929
|
3030
LL | format!("{0} {1}", 1);
31-
| ^^^ ^^^
31+
| ^^^
3232
|
3333
= note: positional arguments are zero-based
3434

3535
error: invalid reference to positional argument 2 (there are 2 arguments)
36-
--> $DIR/ifmt-bad-arg.rs:29:14
36+
--> $DIR/ifmt-bad-arg.rs:29:22
3737
|
3838
LL | format!("{0} {1} {2}", 1, 2);
39-
| ^^^ ^^^ ^^^
39+
| ^^^
4040
|
4141
= note: positional arguments are zero-based
4242

4343
error: invalid reference to positional argument 2 (there are 2 arguments)
44-
--> $DIR/ifmt-bad-arg.rs:32:14
44+
--> $DIR/ifmt-bad-arg.rs:32:28
4545
|
4646
LL | format!("{} {value} {} {}", 1, value=2);
47-
| ^^ ^^^^^^^ ^^ ^^
47+
| ^^
4848
|
4949
= note: positional arguments are zero-based
5050

5151
error: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments)
52-
--> $DIR/ifmt-bad-arg.rs:34:14
52+
--> $DIR/ifmt-bad-arg.rs:34:38
5353
|
5454
LL | format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2);
55-
| ^^^^^^ ^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^
55+
| ^^ ^^ ^^
5656
|
5757
= note: positional arguments are zero-based
5858

0 commit comments

Comments
 (0)