Skip to content

Commit 1ace7f0

Browse files
committed
syntax_ext: format: resolve named arguments early
Converts named argument references into indices, right after verification as suggested by @alexcrichton. This drastically simplifies the whole process!
1 parent 0e2a963 commit 1ace7f0

File tree

1 file changed

+62
-85
lines changed

1 file changed

+62
-85
lines changed

src/libsyntax_ext/format.rs

Lines changed: 62 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,12 @@ struct Context<'a, 'b:'a> {
4646

4747
/// Parsed argument expressions and the types that we've found so far for
4848
/// them.
49+
/// Named expressions are resolved early, and are appended to the end of
50+
/// argument expressions.
4951
args: Vec<P<ast::Expr>>,
5052
arg_types: Vec<Option<ArgumentType>>,
51-
/// Parsed named expressions and the types that we've found for them so far.
52-
/// Note that we keep a side-array of the ordering of the named arguments
53-
/// found to be sure that we can translate them in the same order that they
54-
/// were declared in.
55-
names: HashMap<String, P<ast::Expr>>,
56-
name_types: HashMap<String, ArgumentType>,
57-
name_ordering: Vec<String>,
53+
/// Map from named arguments to their resolved indices.
54+
names: HashMap<String, usize>,
5855

5956
/// The latest consecutive literal strings, or empty if there weren't any.
6057
literal: String,
@@ -66,8 +63,6 @@ struct Context<'a, 'b:'a> {
6663
/// Stays `true` if all formatting parameters are default (as in "{}{}").
6764
all_pieces_simple: bool,
6865

69-
name_positions: HashMap<String, usize>,
70-
7166
/// Current position of the implicit positional arg pointer, as if it
7267
/// still existed in this phase of processing.
7368
/// Used only for `all_pieces_simple` tracking in `trans_piece`.
@@ -80,15 +75,12 @@ struct Context<'a, 'b:'a> {
8075
///
8176
/// If parsing succeeds, the return value is:
8277
/// ```ignore
83-
/// Some((fmtstr, unnamed arguments, ordering of named arguments,
84-
/// named arguments))
78+
/// Some((fmtstr, parsed arguments, index map for named arguments))
8579
/// ```
8680
fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
87-
-> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<String>,
88-
HashMap<String, P<ast::Expr>>)> {
89-
let mut args = Vec::new();
90-
let mut names = HashMap::<String, P<ast::Expr>>::new();
91-
let mut order = Vec::new();
81+
-> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, HashMap<String, usize>)> {
82+
let mut args = Vec::<P<ast::Expr>>::new();
83+
let mut names = HashMap::<String, usize>::new();
9284

9385
let mut p = ecx.new_parser_from_tts(tts);
9486

@@ -132,20 +124,45 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
132124
ecx.struct_span_err(e.span,
133125
&format!("duplicate argument named `{}`",
134126
name))
135-
.span_note(prev.span, "previously here")
127+
.span_note(args[*prev].span, "previously here")
136128
.emit();
137129
continue;
138130
}
139-
order.push(name.to_string());
140-
names.insert(name.to_string(), e);
131+
132+
// Resolve names into slots early.
133+
// Since all the positional args are already seen at this point
134+
// if the input is valid, we can simply append to the positional
135+
// args. And remember the names.
136+
let slot = args.len();
137+
names.insert(name.to_string(), slot);
138+
args.push(e);
141139
} else {
142140
args.push(panictry!(p.parse_expr()));
143141
}
144142
}
145-
Some((fmtstr, args, order, names))
143+
Some((fmtstr, args, names))
146144
}
147145

148146
impl<'a, 'b> Context<'a, 'b> {
147+
fn resolve_name_inplace(&self, p: &mut parse::Piece) {
148+
let lookup = |s| *self.names.get(s).unwrap_or(&0);
149+
150+
match *p {
151+
parse::String(_) => {}
152+
parse::NextArgument(ref mut arg) => {
153+
if let parse::ArgumentNamed(s) = arg.position {
154+
arg.position = parse::ArgumentIs(lookup(s));
155+
}
156+
if let parse::CountIsName(s) = arg.format.width {
157+
arg.format.width = parse::CountIsParam(lookup(s));
158+
}
159+
if let parse::CountIsName(s) = arg.format.precision {
160+
arg.format.precision = parse::CountIsParam(lookup(s));
161+
}
162+
}
163+
}
164+
}
165+
149166
/// Verifies one piece of a parse string. All errors are not emitted as
150167
/// fatal so we can continue giving errors about this and possibly other
151168
/// format strings.
@@ -214,24 +231,16 @@ impl<'a, 'b> Context<'a, 'b> {
214231
}
215232

216233
Named(name) => {
217-
let span = match self.names.get(&name) {
218-
Some(e) => e.span,
234+
let idx = match self.names.get(&name) {
235+
Some(e) => *e,
219236
None => {
220237
let msg = format!("there is no argument named `{}`", name);
221238
self.ecx.span_err(self.fmtsp, &msg[..]);
222239
return;
223240
}
224241
};
225-
self.verify_same(span, &ty, self.name_types.get(&name));
226-
if !self.name_types.contains_key(&name) {
227-
self.name_types.insert(name.clone(), ty);
228-
}
229-
// Assign this named argument a slot in the arguments array if
230-
// it hasn't already been assigned a slot.
231-
if !self.name_positions.contains_key(&name) {
232-
let slot = self.name_positions.len();
233-
self.name_positions.insert(name, slot);
234-
}
242+
// Treat as positional arg.
243+
self.verify_arg_type(Exact(idx), ty)
235244
}
236245
}
237246
}
@@ -301,14 +310,8 @@ impl<'a, 'b> Context<'a, 'b> {
301310
count("Param", Some(self.ecx.expr_usize(sp, i)))
302311
}
303312
parse::CountImplied => count("Implied", None),
304-
parse::CountIsName(n) => {
305-
let i = match self.name_positions.get(n) {
306-
Some(&i) => i,
307-
None => 0, // error already emitted elsewhere
308-
};
309-
let i = i + self.args.len();
310-
count("Param", Some(self.ecx.expr_usize(sp, i)))
311-
}
313+
// should never be the case, names are already resolved
314+
parse::CountIsName(_) => panic!("should never happen"),
312315
}
313316
}
314317

@@ -348,16 +351,9 @@ impl<'a, 'b> Context<'a, 'b> {
348351
match arg.position {
349352
parse::ArgumentIs(i) => pos("At", Some(i)),
350353

351-
// Named arguments are converted to positional arguments
352-
// at the end of the list of arguments
353-
parse::ArgumentNamed(n) => {
354-
let i = match self.name_positions.get(n) {
355-
Some(&i) => i,
356-
None => 0, // error already emitted elsewhere
357-
};
358-
let i = i + self.args.len();
359-
pos("At", Some(i))
360-
}
354+
// should never be the case, because names are already
355+
// resolved.
356+
parse::ArgumentNamed(_) => panic!("should never happen"),
361357
}
362358
};
363359

@@ -448,7 +444,6 @@ impl<'a, 'b> Context<'a, 'b> {
448444
/// to
449445
fn into_expr(mut self) -> P<ast::Expr> {
450446
let mut locals = Vec::new();
451-
let mut names = vec![None; self.name_positions.len()];
452447
let mut pats = Vec::new();
453448
let mut heads = Vec::new();
454449

@@ -485,29 +480,11 @@ impl<'a, 'b> Context<'a, 'b> {
485480
self.ecx.expr_ident(e.span, name)));
486481
heads.push(self.ecx.expr_addr_of(e.span, e));
487482
}
488-
for name in &self.name_ordering {
489-
let e = match self.names.remove(name) {
490-
Some(e) => e,
491-
None => continue
492-
};
493-
let arg_ty = match self.name_types.get(name) {
494-
Some(ty) => ty,
495-
None => continue
496-
};
497-
498-
let lname = self.ecx.ident_of(&format!("__arg{}",
499-
*name));
500-
pats.push(self.ecx.pat_ident(DUMMY_SP, lname));
501-
names[*self.name_positions.get(name).unwrap()] =
502-
Some(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
503-
self.ecx.expr_ident(e.span, lname)));
504-
heads.push(self.ecx.expr_addr_of(e.span, e));
505-
}
506483

507484
// Now create a vector containing all the arguments
508-
let args = locals.into_iter().chain(names.into_iter().map(|a| a.unwrap()));
485+
let args = locals.into_iter().collect();
509486

510-
let args_array = self.ecx.expr_vec(self.fmtsp, args.collect());
487+
let args_array = self.ecx.expr_vec(self.fmtsp, args);
511488

512489
// Constructs an AST equivalent to:
513490
//
@@ -605,9 +582,9 @@ pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
605582
-> Box<base::MacResult+'cx> {
606583

607584
match parse_args(ecx, sp, tts) {
608-
Some((efmt, args, order, names)) => {
585+
Some((efmt, args, names)) => {
609586
MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt,
610-
args, order, names))
587+
args, names))
611588
}
612589
None => DummyResult::expr(sp)
613590
}
@@ -618,8 +595,7 @@ pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
618595
pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
619596
efmt: P<ast::Expr>,
620597
args: Vec<P<ast::Expr>>,
621-
name_ordering: Vec<String>,
622-
names: HashMap<String, P<ast::Expr>>)
598+
names: HashMap<String, usize>)
623599
-> P<ast::Expr> {
624600
let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect();
625601
let macsp = ecx.call_site();
@@ -632,9 +608,6 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
632608
args: args,
633609
arg_types: arg_types,
634610
names: names,
635-
name_positions: HashMap::new(),
636-
name_types: HashMap::new(),
637-
name_ordering: name_ordering,
638611
curarg: 0,
639612
literal: String::new(),
640613
pieces: Vec::new(),
@@ -655,9 +628,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
655628

656629
loop {
657630
match parser.next() {
658-
Some(piece) => {
631+
Some(mut piece) => {
659632
if !parser.errors.is_empty() { break }
660633
cx.verify_piece(&piece);
634+
cx.resolve_name_inplace(&mut piece);
661635
pieces.push(piece);
662636
}
663637
None => break
@@ -683,14 +657,17 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
683657
}
684658

685659
// Make sure that all arguments were used and all arguments have types.
660+
let num_pos_args = cx.args.len() - cx.names.len();
686661
for (i, ty) in cx.arg_types.iter().enumerate() {
687662
if ty.is_none() {
688-
cx.ecx.span_err(cx.args[i].span, "argument never used");
689-
}
690-
}
691-
for (name, e) in &cx.names {
692-
if !cx.name_types.contains_key(name) {
693-
cx.ecx.span_err(e.span, "named argument never used");
663+
let msg = if i >= num_pos_args {
664+
// named argument
665+
"named argument never used"
666+
} else {
667+
// positional argument
668+
"argument never used"
669+
};
670+
cx.ecx.span_err(cx.args[i].span, msg);
694671
}
695672
}
696673

0 commit comments

Comments
 (0)