Skip to content

Commit f457e6c

Browse files
committed
syntax_ext: format: process counts uniquely and separately
1 parent 5e55a44 commit f457e6c

File tree

1 file changed

+62
-11
lines changed

1 file changed

+62
-11
lines changed

src/libsyntax_ext/format.rs

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use syntax_pos::{Span, DUMMY_SP};
2424
use syntax::tokenstream;
2525

2626
use std::collections::HashMap;
27+
use std::collections::hash_map::Entry;
2728

2829
#[derive(PartialEq)]
2930
enum ArgumentType {
@@ -70,6 +71,12 @@ struct Context<'a, 'b:'a> {
7071
/// mapping in `trans_piece`.
7172
arg_index_map: Vec<usize>,
7273

74+
count_args_index_offset: usize,
75+
76+
count_args: Vec<Position>,
77+
count_positions: HashMap<usize, usize>,
78+
count_positions_count: usize,
79+
7380
/// Current position of the implicit positional arg pointer, as if it
7481
/// still existed in this phase of processing.
7582
/// Used only for `all_pieces_simple` tracking in `trans_piece`.
@@ -225,7 +232,22 @@ impl<'a, 'b> Context<'a, 'b> {
225232
self.ecx.span_err(self.fmtsp, &msg[..]);
226233
return;
227234
}
228-
self.arg_types[arg].push(ty);
235+
match ty {
236+
Placeholder(_) => {
237+
self.arg_types[arg].push(ty);
238+
}
239+
Count => {
240+
match self.count_positions.entry(arg) {
241+
Entry::Vacant(e) => {
242+
let i = self.count_positions_count;
243+
e.insert(i);
244+
self.count_args.push(Exact(arg));
245+
self.count_positions_count += 1;
246+
}
247+
Entry::Occupied(_) => {}
248+
}
249+
}
250+
}
229251
}
230252

231253
Named(name) => {
@@ -255,15 +277,17 @@ impl<'a, 'b> Context<'a, 'b> {
255277
self.arg_index_map.push(sofar);
256278
sofar += self.arg_types[i].len();
257279
}
280+
281+
// Record starting index for counts, which appear just
282+
// after the positional args
283+
self.count_args_index_offset = sofar;
258284
}
259285

260286
fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
261287
ecx.std_path(&["fmt", "rt", "v1", s])
262288
}
263289

264-
fn trans_count(&self,
265-
c: parse::Count,
266-
arg_index_consumed: &mut Vec<usize>) -> P<ast::Expr> {
290+
fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
267291
let sp = self.macsp;
268292
let count = |c, arg| {
269293
let mut path = Context::rtpath(self.ecx, "Count");
@@ -278,9 +302,12 @@ impl<'a, 'b> Context<'a, 'b> {
278302
parse::CountIsParam(i) => {
279303
// This needs mapping too, as `i` is referring to a macro
280304
// argument.
281-
let arg_idx = self.arg_index_map[i] + arg_index_consumed[i];
282-
arg_index_consumed[i] += 1;
283-
count("Param", Some(self.ecx.expr_usize(sp, arg_idx)))
305+
let i = match self.count_positions.get(&i) {
306+
Some(&i) => i,
307+
None => 0, // error already emitted elsewhere
308+
};
309+
let i = i + self.count_args_index_offset;
310+
count("Param", Some(self.ecx.expr_usize(sp, i)))
284311
}
285312
parse::CountImplied => count("Implied", None),
286313
// should never be the case, names are already resolved
@@ -383,8 +410,8 @@ impl<'a, 'b> Context<'a, 'b> {
383410
};
384411
let align = self.ecx.expr_path(align);
385412
let flags = self.ecx.expr_u32(sp, arg.format.flags);
386-
let prec = self.trans_count(arg.format.precision, arg_index_consumed);
387-
let width = self.trans_count(arg.format.width, arg_index_consumed);
413+
let prec = self.trans_count(arg.format.precision);
414+
let width = self.trans_count(arg.format.width);
388415
let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
389416
let fmt = self.ecx.expr_struct(sp, path, vec!(
390417
self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
@@ -431,6 +458,7 @@ impl<'a, 'b> Context<'a, 'b> {
431458
/// to
432459
fn into_expr(mut self) -> P<ast::Expr> {
433460
let mut locals = Vec::new();
461+
let mut counts = Vec::new();
434462
let mut pats = Vec::new();
435463
let mut heads = Vec::new();
436464

@@ -447,6 +475,10 @@ impl<'a, 'b> Context<'a, 'b> {
447475
piece_ty,
448476
self.str_pieces);
449477

478+
// Before consuming the expressions, we have to remember spans for
479+
// count arguments as they are now generated separate from other
480+
// arguments, hence have no access to the `P<ast::Expr>`'s.
481+
let spans_pos: Vec<_> = self.args.iter().map(|e| e.span.clone()).collect();
450482

451483
// Right now there is a bug such that for the expression:
452484
// foo(bar(&1))
@@ -464,11 +496,23 @@ impl<'a, 'b> Context<'a, 'b> {
464496
}
465497
heads.push(self.ecx.expr_addr_of(e.span, e));
466498
}
499+
for pos in self.count_args {
500+
let name = self.ecx.ident_of(&match pos {
501+
Exact(i) => format!("__arg{}", i),
502+
_ => panic!("should never happen"),
503+
});
504+
let span = match pos {
505+
Exact(i) => spans_pos[i],
506+
_ => panic!("should never happen"),
507+
};
508+
counts.push(Context::format_arg(self.ecx, self.macsp, span, &Count,
509+
self.ecx.expr_ident(span, name)));
510+
}
467511

468512
// Now create a vector containing all the arguments
469-
let args = locals.into_iter().collect();
513+
let args = locals.into_iter().chain(counts.into_iter());
470514

471-
let args_array = self.ecx.expr_vec(self.fmtsp, args);
515+
let args_array = self.ecx.expr_vec(self.fmtsp, args.collect());
472516

473517
// Constructs an AST equivalent to:
474518
//
@@ -594,6 +638,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
594638
names: names,
595639
curarg: 0,
596640
arg_index_map: Vec::new(),
641+
count_args: Vec::new(),
642+
count_positions: HashMap::new(),
643+
count_positions_count: 0,
644+
count_args_index_offset: 0,
597645
literal: String::new(),
598646
pieces: Vec::new(),
599647
str_pieces: Vec::new(),
@@ -648,6 +696,9 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
648696
let num_pos_args = cx.args.len() - cx.names.len();
649697
for (i, ty) in cx.arg_types.iter().enumerate() {
650698
if ty.len() == 0 {
699+
if cx.count_positions.contains_key(&i) {
700+
continue;
701+
}
651702
let msg = if i >= num_pos_args {
652703
// named argument
653704
"named argument never used"

0 commit comments

Comments
 (0)