1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 use codemap::{Span, respan};
16 use ext::build::AstBuilder;
18 use parse::token::InternedString;
22 use std::hashmap::{HashMap, HashSet};
38 ecx: &'a mut ExtCtxt<'a>,
41 // Parsed argument expressions and the types that we've found so far for
44 arg_types: ~[Option<ArgumentType>],
45 // Parsed named expressions and the types that we've found for them so far
46 names: HashMap<~str, @ast::Expr>,
47 name_types: HashMap<~str, ArgumentType>,
49 // Collection of the compiled `rt::Piece` structures
50 pieces: ~[@ast::Expr],
51 name_positions: HashMap<~str, uint>,
52 method_statics: ~[@ast::Item],
54 // Updated as arguments are consumed or methods are entered
59 impl<'a> Context<'a> {
60 /// Parses the arguments from the given list of tokens, returning None if
61 /// there's a parse error so we can continue parsing other format! expressions.
62 fn parse_args(&mut self, sp: Span, tts: &[ast::TokenTree])
63 -> (@ast::Expr, Option<@ast::Expr>) {
64 let mut p = rsparse::new_parser_from_tts(self.ecx.parse_sess(),
67 // Parse the leading function expression (maybe a block, maybe a path)
68 let extra = p.parse_expr();
69 if !p.eat(&token::COMMA) {
70 self.ecx.span_err(sp, "expected token: `,`");
74 if p.token == token::EOF {
75 self.ecx.span_err(sp, "requires at least a format string argument");
78 let fmtstr = p.parse_expr();
79 let mut named = false;
80 while p.token != token::EOF {
81 if !p.eat(&token::COMMA) {
82 self.ecx.span_err(sp, "expected token: `,`");
85 if p.token == token::EOF { break } // accept trailing commas
86 if named || (token::is_ident(&p.token) &&
87 p.look_ahead(1, |t| *t == token::EQ)) {
89 let ident = match p.token {
90 token::IDENT(i, _) => {
95 self.ecx.span_err(p.span,
96 "expected ident, positional arguments \
97 cannot follow named arguments");
101 self.ecx.span_err(p.span,
102 format!("expected ident for named \
103 argument, but found `{}`",
104 p.this_token_to_str()));
105 return (extra, None);
108 let interned_name = token::get_ident(ident.name);
109 let name = interned_name.get();
110 p.expect(&token::EQ);
111 let e = p.parse_expr();
112 match self.names.find_equiv(&name) {
115 self.ecx.span_err(e.span, format!("duplicate argument \
117 self.ecx.parse_sess.span_diagnostic.span_note(
118 prev.span, "previously here");
122 self.names.insert(name.to_str(), e);
124 self.args.push(p.parse_expr());
125 self.arg_types.push(None);
128 return (extra, Some(fmtstr));
131 /// Verifies one piece of a parse string. All errors are not emitted as
132 /// fatal so we can continue giving errors about this and possibly other
134 fn verify_piece(&mut self, p: &parse::Piece) {
136 parse::String(..) => {}
137 parse::CurrentArgument => {
138 if self.nest_level == 0 {
139 self.ecx.span_err(self.fmtsp,
140 "`#` reference used with nothing to \
144 parse::Argument(ref arg) => {
145 // width/precision first, if they have implicit positional
146 // parameters it makes more sense to consume them first.
147 self.verify_count(arg.format.width);
148 self.verify_count(arg.format.precision);
150 // argument second, if it's an implicit positional parameter
151 // it's written second, so it should come after width/precision.
152 let pos = match arg.position {
153 parse::ArgumentNext => {
154 let i = self.next_arg;
155 if self.check_positional_ok() {
160 parse::ArgumentIs(i) => Exact(i),
161 parse::ArgumentNamed(s) => Named(s.to_str()),
164 // and finally the method being applied
167 let ty = Known(arg.format.ty.to_str());
168 self.verify_arg_type(pos, ty);
170 Some(ref method) => { self.verify_method(pos, *method); }
176 fn verify_pieces(&mut self, pieces: &[parse::Piece]) {
177 for piece in pieces.iter() {
178 self.verify_piece(piece);
182 fn verify_count(&mut self, c: parse::Count) {
184 parse::CountImplied | parse::CountIs(..) => {}
185 parse::CountIsParam(i) => {
186 self.verify_arg_type(Exact(i), Unsigned);
188 parse::CountIsName(s) => {
189 self.verify_arg_type(Named(s.to_str()), Unsigned);
191 parse::CountIsNextParam => {
192 if self.check_positional_ok() {
193 self.verify_arg_type(Exact(self.next_arg), Unsigned);
200 fn check_positional_ok(&mut self) -> bool {
201 if self.nest_level != 0 {
202 self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
203 arguments nested inside methods");
210 fn verify_method(&mut self, pos: Position, m: &parse::Method) {
211 self.nest_level += 1;
213 parse::Plural(_, ref arms, ref default) => {
214 let mut seen_cases = HashSet::new();
215 self.verify_arg_type(pos, Unsigned);
216 for arm in arms.iter() {
217 if !seen_cases.insert(arm.selector) {
219 parse::Keyword(name) => {
220 self.ecx.span_err(self.fmtsp,
221 format!("duplicate selector \
224 parse::Literal(idx) => {
225 self.ecx.span_err(self.fmtsp,
226 format!("duplicate selector \
231 self.verify_pieces(arm.result);
233 self.verify_pieces(*default);
235 parse::Select(ref arms, ref default) => {
236 self.verify_arg_type(pos, String);
237 let mut seen_cases = HashSet::new();
238 for arm in arms.iter() {
239 if !seen_cases.insert(arm.selector) {
240 self.ecx.span_err(self.fmtsp,
241 format!("duplicate selector `{}`",
243 } else if arm.selector == "" {
244 self.ecx.span_err(self.fmtsp,
245 "empty selector in `select`");
247 self.verify_pieces(arm.result);
249 self.verify_pieces(*default);
252 self.nest_level -= 1;
255 fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
258 if arg < 0 || self.args.len() <= arg {
259 let msg = format!("invalid reference to argument `{}` (there \
260 are {} arguments)", arg, self.args.len());
261 self.ecx.span_err(self.fmtsp, msg);
265 let arg_type = match self.arg_types[arg] {
267 Some(ref x) => Some(x)
269 self.verify_same(self.args[arg].span, &ty, arg_type);
271 if self.arg_types[arg].is_none() {
272 self.arg_types[arg] = Some(ty);
277 let span = match self.names.find(&name) {
280 let msg = format!("there is no argument named `{}`", name);
281 self.ecx.span_err(self.fmtsp, msg);
285 self.verify_same(span, &ty, self.name_types.find(&name));
286 if !self.name_types.contains_key(&name) {
287 self.name_types.insert(name.clone(), ty);
289 // Assign this named argument a slot in the arguments array if
290 // it hasn't already been assigned a slot.
291 if !self.name_positions.contains_key(&name) {
292 let slot = self.name_positions.len();
293 self.name_positions.insert(name, slot);
299 /// When we're keeping track of the types that are declared for certain
300 /// arguments, we assume that `None` means we haven't seen this argument
301 /// yet, `Some(None)` means that we've seen the argument, but no format was
302 /// specified, and `Some(Some(x))` means that the argument was declared to
305 /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
306 /// that: `Some(None) == Some(Some(x))`
307 fn verify_same(&self,
310 before: Option<&ArgumentType>) {
311 let cur = match before {
319 (&Known(ref cur), &Known(ref ty)) => {
320 self.ecx.span_err(sp,
321 format!("argument redeclared with type `{}` when \
322 it was previously `{}`",
326 (&Known(ref cur), _) => {
327 self.ecx.span_err(sp,
328 format!("argument used to format with `{}` was \
329 attempted to not be used for formatting",
332 (_, &Known(ref ty)) => {
333 self.ecx.span_err(sp,
334 format!("argument previously used as a format \
335 argument attempted to be used as `{}`",
339 self.ecx.span_err(sp, "argument declared with multiple formats");
344 /// These attributes are applied to all statics that this syntax extension
346 fn static_attrs(&self) -> ~[ast::Attribute] {
347 // Flag statics as `address_insignificant` so LLVM can merge duplicate
348 // globals as much as possible (which we're generating a whole lot of).
349 let unnamed = self.ecx
350 .meta_word(self.fmtsp,
352 "address_insignificant"));
353 let unnamed = self.ecx.attribute(self.fmtsp, unnamed);
355 // Do not warn format string as dead code
356 let dead_code = self.ecx.meta_word(self.fmtsp,
357 InternedString::new("dead_code"));
358 let allow_dead_code = self.ecx.meta_list(self.fmtsp,
359 InternedString::new("allow"),
361 let allow_dead_code = self.ecx.attribute(self.fmtsp, allow_dead_code);
362 return ~[unnamed, allow_dead_code];
365 /// Translate a `parse::Piece` to a static `rt::Piece`
366 fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr {
368 let parsepath = |s: &str| {
369 ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
370 self.ecx.ident_of("parse"), self.ecx.ident_of(s)]
372 let rtpath = |s: &str| {
373 ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
374 self.ecx.ident_of("rt"), self.ecx.ident_of(s)]
376 let ctpath = |s: &str| {
377 ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
378 self.ecx.ident_of("parse"), self.ecx.ident_of(s)]
380 let none = self.ecx.path_global(sp, ~[
381 self.ecx.ident_of("std"),
382 self.ecx.ident_of("option"),
383 self.ecx.ident_of("None")]);
384 let none = self.ecx.expr_path(none);
385 let some = |e: @ast::Expr| {
386 let p = self.ecx.path_global(sp, ~[
387 self.ecx.ident_of("std"),
388 self.ecx.ident_of("option"),
389 self.ecx.ident_of("Some")]);
390 let p = self.ecx.expr_path(p);
391 self.ecx.expr_call(sp, p, ~[e])
393 let trans_count = |c: parse::Count| {
395 parse::CountIs(i) => {
396 self.ecx.expr_call_global(sp, rtpath("CountIs"),
397 ~[self.ecx.expr_uint(sp, i)])
399 parse::CountIsParam(i) => {
400 self.ecx.expr_call_global(sp, rtpath("CountIsParam"),
401 ~[self.ecx.expr_uint(sp, i)])
403 parse::CountImplied => {
404 let path = self.ecx.path_global(sp, rtpath("CountImplied"));
405 self.ecx.expr_path(path)
407 parse::CountIsNextParam => {
408 let path = self.ecx.path_global(sp, rtpath("CountIsNextParam"));
409 self.ecx.expr_path(path)
411 parse::CountIsName(n) => {
412 let i = match self.name_positions.find_equiv(&n) {
414 None => 0, // error already emitted elsewhere
416 let i = i + self.args.len();
417 self.ecx.expr_call_global(sp, rtpath("CountIsParam"),
418 ~[self.ecx.expr_uint(sp, i)])
422 let trans_method = |method: &parse::Method| {
423 let method = match *method {
424 parse::Select(ref arms, ref default) => {
425 let arms = arms.iter().map(|arm| {
426 let p = self.ecx.path_global(sp, rtpath("SelectArm"));
427 let result = arm.result.iter().map(|p| {
430 let s = token::intern_and_get_ident(arm.selector);
431 let selector = self.ecx.expr_str(sp, s);
432 self.ecx.expr_struct(sp, p, ~[
433 self.ecx.field_imm(sp,
434 self.ecx.ident_of("selector"),
436 self.ecx.field_imm(sp, self.ecx.ident_of("result"),
437 self.ecx.expr_vec_slice(sp, result)),
440 let default = default.iter().map(|p| {
443 self.ecx.expr_call_global(sp, rtpath("Select"), ~[
444 self.ecx.expr_vec_slice(sp, arms),
445 self.ecx.expr_vec_slice(sp, default),
448 parse::Plural(offset, ref arms, ref default) => {
449 let offset = match offset {
450 Some(i) => { some(self.ecx.expr_uint(sp, i)) }
451 None => { none.clone() }
453 let arms = arms.iter().map(|arm| {
454 let p = self.ecx.path_global(sp, rtpath("PluralArm"));
455 let result = arm.result.iter().map(|p| {
458 let (lr, selarg) = match arm.selector {
459 parse::Keyword(t) => {
460 let p = ctpath(format!("{:?}", t));
461 let p = self.ecx.path_global(sp, p);
462 (rtpath("Keyword"), self.ecx.expr_path(p))
464 parse::Literal(i) => {
465 (rtpath("Literal"), self.ecx.expr_uint(sp, i))
468 let selector = self.ecx.expr_call_global(sp,
470 self.ecx.expr_struct(sp, p, ~[
471 self.ecx.field_imm(sp,
472 self.ecx.ident_of("selector"),
474 self.ecx.field_imm(sp, self.ecx.ident_of("result"),
475 self.ecx.expr_vec_slice(sp, result)),
478 let default = default.iter().map(|p| {
481 self.ecx.expr_call_global(sp, rtpath("Plural"), ~[
483 self.ecx.expr_vec_slice(sp, arms),
484 self.ecx.expr_vec_slice(sp, default),
488 let life = self.ecx.lifetime(sp, self.ecx.ident_of("static"));
489 let ty = self.ecx.ty_path(self.ecx.path_all(
496 let st = ast::ItemStatic(ty, ast::MutImmutable, method);
497 let static_name = self.ecx.ident_of(format!("__STATIC_METHOD_{}",
498 self.method_statics.len()));
499 let item = self.ecx.item(sp, static_name, self.static_attrs(), st);
500 self.method_statics.push(item);
501 self.ecx.expr_ident(sp, static_name)
505 parse::String(s) => {
506 let s = token::intern_and_get_ident(s);
507 self.ecx.expr_call_global(sp,
510 self.ecx.expr_str(sp, s)
513 parse::CurrentArgument => {
514 let nil = self.ecx.expr_lit(sp, ast::LitNil);
515 self.ecx.expr_call_global(sp, rtpath("CurrentArgument"), ~[nil])
517 parse::Argument(ref arg) => {
518 // Translate the position
519 let pos = match arg.position {
520 // These two have a direct mapping
521 parse::ArgumentNext => {
522 let path = self.ecx.path_global(sp,
523 rtpath("ArgumentNext"));
524 self.ecx.expr_path(path)
526 parse::ArgumentIs(i) => {
527 self.ecx.expr_call_global(sp, rtpath("ArgumentIs"),
528 ~[self.ecx.expr_uint(sp, i)])
530 // Named arguments are converted to positional arguments at
531 // the end of the list of arguments
532 parse::ArgumentNamed(n) => {
533 let i = match self.name_positions.find_equiv(&n) {
535 None => 0, // error already emitted elsewhere
537 let i = i + self.args.len();
538 self.ecx.expr_call_global(sp, rtpath("ArgumentIs"),
539 ~[self.ecx.expr_uint(sp, i)])
543 // Translate the format
544 let fill = match arg.format.fill { Some(c) => c, None => ' ' };
545 let fill = self.ecx.expr_lit(sp, ast::LitChar(fill as u32));
546 let align = match arg.format.align {
547 parse::AlignLeft => {
548 self.ecx.path_global(sp, parsepath("AlignLeft"))
550 parse::AlignRight => {
551 self.ecx.path_global(sp, parsepath("AlignRight"))
553 parse::AlignUnknown => {
554 self.ecx.path_global(sp, parsepath("AlignUnknown"))
557 let align = self.ecx.expr_path(align);
558 let flags = self.ecx.expr_uint(sp, arg.format.flags);
559 let prec = trans_count(arg.format.precision);
560 let width = trans_count(arg.format.width);
561 let path = self.ecx.path_global(sp, rtpath("FormatSpec"));
562 let fmt = self.ecx.expr_struct(sp, path, ~[
563 self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
564 self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
565 self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
566 self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
567 self.ecx.field_imm(sp, self.ecx.ident_of("width"), width),
570 // Translate the method (if any)
571 let method = match arg.method {
572 None => { none.clone() }
574 let m = trans_method(*m);
575 some(self.ecx.expr_addr_of(sp, m))
578 let path = self.ecx.path_global(sp, rtpath("Argument"));
579 let s = self.ecx.expr_struct(sp, path, ~[
580 self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
581 self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt),
582 self.ecx.field_imm(sp, self.ecx.ident_of("method"), method),
584 self.ecx.expr_call_global(sp, rtpath("Argument"), ~[s])
589 /// Actually builds the expression which the iformat! block will be expanded
591 fn to_expr(&self, extra: @ast::Expr) -> @ast::Expr {
593 let mut locals = ~[];
594 let mut names = vec::from_fn(self.name_positions.len(), |_| None);
596 // First, declare all of our methods that are statics
597 for &method in self.method_statics.iter() {
598 let decl = respan(self.fmtsp, ast::DeclItem(method));
599 lets.push(@respan(self.fmtsp,
600 ast::StmtDecl(@decl, ast::DUMMY_NODE_ID)));
603 // Next, build up the static array which will become our precompiled
605 let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
606 let piece_ty = self.ecx.ty_path(self.ecx.path_all(
609 self.ecx.ident_of("std"),
610 self.ecx.ident_of("fmt"),
611 self.ecx.ident_of("rt"),
612 self.ecx.ident_of("Piece"),
615 self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("static"))),
618 let ty = ast::TyFixedLengthVec(
620 self.ecx.expr_uint(self.fmtsp, self.pieces.len())
622 let ty = self.ecx.ty(self.fmtsp, ty);
623 let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
624 let static_name = self.ecx.ident_of("__STATIC_FMTSTR");
625 let item = self.ecx.item(self.fmtsp, static_name,
626 self.static_attrs(), st);
627 let decl = respan(self.fmtsp, ast::DeclItem(item));
628 lets.push(@respan(self.fmtsp, ast::StmtDecl(@decl, ast::DUMMY_NODE_ID)));
630 // Right now there is a bug such that for the expression:
632 // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
633 // vald for the call to `foo`. To work around this all arguments to the
634 // format! string are shoved into locals. Furthermore, we shove the address
635 // of each variable because we don't want to move out of the arguments
636 // passed to this function.
637 for (i, &e) in self.args.iter().enumerate() {
638 if self.arg_types[i].is_none() { continue } // error already generated
640 let name = self.ecx.ident_of(format!("__arg{}", i));
641 let e = self.ecx.expr_addr_of(e.span, e);
642 lets.push(self.ecx.stmt_let(e.span, false, name, e));
643 locals.push(self.format_arg(e.span, Exact(i),
644 self.ecx.expr_ident(e.span, name)));
646 for (name, &e) in self.names.iter() {
647 if !self.name_types.contains_key(name) {
651 let lname = self.ecx.ident_of(format!("__arg{}", *name));
652 let e = self.ecx.expr_addr_of(e.span, e);
653 lets.push(self.ecx.stmt_let(e.span, false, lname, e));
654 names[*self.name_positions.get(name)] =
655 Some(self.format_arg(e.span,
656 Named((*name).clone()),
657 self.ecx.expr_ident(e.span, lname)));
660 // Now create a vector containing all the arguments
661 let slicename = self.ecx.ident_of("__args_vec");
663 let args = names.move_iter().map(|a| a.unwrap());
664 let mut args = locals.move_iter().chain(args);
665 let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
666 lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
669 // Now create the fmt::Arguments struct with all our locals we created.
670 let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
671 let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
672 let result = self.ecx.expr_call_global(self.fmtsp, ~[
673 self.ecx.ident_of("std"),
674 self.ecx.ident_of("fmt"),
675 self.ecx.ident_of("Arguments"),
676 self.ecx.ident_of("new"),
677 ], ~[fmt, args_slice]);
679 // We did all the work of making sure that the arguments
680 // structure is safe, so we can safely have an unsafe block.
681 let result = self.ecx.expr_block(P(ast::Block {
685 id: ast::DUMMY_NODE_ID,
686 rules: ast::UnsafeBlock(ast::CompilerGenerated),
689 let resname = self.ecx.ident_of("__args");
690 lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
691 let res = self.ecx.expr_ident(self.fmtsp, resname);
692 let result = self.ecx.expr_call(extra.span, extra, ~[
693 self.ecx.expr_addr_of(extra.span, res)]);
694 self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
698 fn format_arg(&self, sp: Span, argno: Position, arg: @ast::Expr)
700 let ty = match argno {
701 Exact(ref i) => self.arg_types[*i].get_ref(),
702 Named(ref s) => self.name_types.get(s)
705 let fmt_trait = match *ty {
706 Known(ref tyname) => {
707 match (*tyname).as_slice() {
712 "d" | "i" => "Signed",
724 self.ecx.span_err(sp,
725 format!("unknown format trait `{}`",
732 return self.ecx.expr_call_global(sp, ~[
733 self.ecx.ident_of("std"),
734 self.ecx.ident_of("fmt"),
735 self.ecx.ident_of("argumentstr"),
739 return self.ecx.expr_call_global(sp, ~[
740 self.ecx.ident_of("std"),
741 self.ecx.ident_of("fmt"),
742 self.ecx.ident_of("argumentuint"),
747 let format_fn = self.ecx.path_global(sp, ~[
748 self.ecx.ident_of("std"),
749 self.ecx.ident_of("fmt"),
750 self.ecx.ident_of(fmt_trait),
751 self.ecx.ident_of("fmt"),
753 self.ecx.expr_call_global(sp, ~[
754 self.ecx.ident_of("std"),
755 self.ecx.ident_of("fmt"),
756 self.ecx.ident_of("argument"),
757 ], ~[self.ecx.expr_path(format_fn), arg])
761 pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
762 tts: &[ast::TokenTree]) -> base::MacResult {
763 let mut cx = Context {
767 names: HashMap::new(),
768 name_positions: HashMap::new(),
769 name_types: HashMap::new(),
776 let (extra, efmt) = match cx.parse_args(sp, tts) {
777 (extra, Some(e)) => (extra, e),
778 (_, None) => { return MRExpr(cx.ecx.expr_uint(sp, 2)); }
780 cx.fmtsp = efmt.span;
781 // Be sure to recursively expand macros just in case the format string uses
782 // a macro to build the format expression.
783 let expr = cx.ecx.expand_expr(efmt);
784 let fmt = match expr_to_str(cx.ecx,
786 "format argument must be a string literal.") {
787 Some((fmt, _)) => fmt,
788 None => return MacResult::dummy_expr()
792 parse::parse_error::cond.trap(|m| {
795 cx.ecx.span_err(efmt.span, m);
798 for piece in parse::Parser::new(fmt.get()) {
800 cx.verify_piece(&piece);
801 let piece = cx.trans_piece(&piece);
802 cx.pieces.push(piece);
806 if err { return MRExpr(efmt) }
808 // Make sure that all arguments were used and all arguments have types.
809 for (i, ty) in cx.arg_types.iter().enumerate() {
811 cx.ecx.span_err(cx.args[i].span, "argument never used");
814 for (name, e) in cx.names.iter() {
815 if !cx.name_types.contains_key(name) {
816 cx.ecx.span_err(e.span, "named argument never used");
820 MRExpr(cx.to_expr(extra))