]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/format.rs
rollup merge of #18407 : thestinger/arena
[rust.git] / src / libsyntax / ext / format.rs
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.
4 //
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.
10
11 use ast;
12 use codemap::{Span, respan};
13 use ext::base::*;
14 use ext::base;
15 use ext::build::AstBuilder;
16 use fmt_macros as parse;
17 use parse::token::InternedString;
18 use parse::token;
19 use ptr::P;
20
21 use std::collections::HashMap;
22 use std::string;
23
24 #[deriving(PartialEq)]
25 enum ArgumentType {
26     Known(string::String),
27     Unsigned
28 }
29
30 enum Position {
31     Exact(uint),
32     Named(string::String),
33 }
34
35 struct Context<'a, 'b:'a> {
36     ecx: &'a mut ExtCtxt<'b>,
37     fmtsp: Span,
38
39     /// Parsed argument expressions and the types that we've found so far for
40     /// them.
41     args: Vec<P<ast::Expr>>,
42     arg_types: Vec<Option<ArgumentType>>,
43     /// Parsed named expressions and the types that we've found for them so far.
44     /// Note that we keep a side-array of the ordering of the named arguments
45     /// found to be sure that we can translate them in the same order that they
46     /// were declared in.
47     names: HashMap<string::String, P<ast::Expr>>,
48     name_types: HashMap<string::String, ArgumentType>,
49     name_ordering: Vec<string::String>,
50
51     /// The latest consecutive literal strings, or empty if there weren't any.
52     literal: string::String,
53
54     /// Collection of the compiled `rt::Argument` structures
55     pieces: Vec<P<ast::Expr>>,
56     /// Collection of string literals
57     str_pieces: Vec<P<ast::Expr>>,
58     /// Stays `true` if all formatting parameters are default (as in "{}{}").
59     all_pieces_simple: bool,
60
61     name_positions: HashMap<string::String, uint>,
62     method_statics: Vec<P<ast::Item>>,
63
64     /// Updated as arguments are consumed or methods are entered
65     nest_level: uint,
66     next_arg: uint,
67 }
68
69 pub enum Invocation {
70     Call(P<ast::Expr>),
71     MethodCall(P<ast::Expr>, ast::Ident),
72 }
73
74 /// Parses the arguments from the given list of tokens, returning None
75 /// if there's a parse error so we can continue parsing other format!
76 /// expressions.
77 ///
78 /// If parsing succeeds, the second return value is:
79 ///
80 ///     Some((fmtstr, unnamed arguments, ordering of named arguments,
81 ///           named arguments))
82 fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
83               tts: &[ast::TokenTree])
84     -> (Invocation, Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<string::String>,
85                             HashMap<string::String, P<ast::Expr>>)>) {
86     let mut args = Vec::new();
87     let mut names = HashMap::<string::String, P<ast::Expr>>::new();
88     let mut order = Vec::new();
89
90     let mut p = ecx.new_parser_from_tts(tts);
91     // Parse the leading function expression (maybe a block, maybe a path)
92     let invocation = if allow_method {
93         let e = p.parse_expr();
94         if !p.eat(&token::Comma) {
95             ecx.span_err(sp, "expected token: `,`");
96             return (Call(e), None);
97         }
98         MethodCall(e, p.parse_ident())
99     } else {
100         Call(p.parse_expr())
101     };
102     if !p.eat(&token::Comma) {
103         ecx.span_err(sp, "expected token: `,`");
104         return (invocation, None);
105     }
106
107     if p.token == token::Eof {
108         ecx.span_err(sp, "requires at least a format string argument");
109         return (invocation, None);
110     }
111     let fmtstr = p.parse_expr();
112     let mut named = false;
113     while p.token != token::Eof {
114         if !p.eat(&token::Comma) {
115             ecx.span_err(sp, "expected token: `,`");
116             return (invocation, None);
117         }
118         if p.token == token::Eof { break } // accept trailing commas
119         if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
120             named = true;
121             let ident = match p.token {
122                 token::Ident(i, _) => {
123                     p.bump();
124                     i
125                 }
126                 _ if named => {
127                     ecx.span_err(p.span,
128                                  "expected ident, positional arguments \
129                                  cannot follow named arguments");
130                     return (invocation, None);
131                 }
132                 _ => {
133                     ecx.span_err(p.span,
134                                  format!("expected ident for named argument, found `{}`",
135                                          p.this_token_to_string()).as_slice());
136                     return (invocation, None);
137                 }
138             };
139             let interned_name = token::get_ident(ident);
140             let name = interned_name.get();
141             p.expect(&token::Eq);
142             let e = p.parse_expr();
143             match names.find_equiv(&name) {
144                 None => {}
145                 Some(prev) => {
146                     ecx.span_err(e.span,
147                                  format!("duplicate argument named `{}`",
148                                          name).as_slice());
149                     ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
150                     continue
151                 }
152             }
153             order.push(name.to_string());
154             names.insert(name.to_string(), e);
155         } else {
156             args.push(p.parse_expr());
157         }
158     }
159     return (invocation, Some((fmtstr, args, order, names)));
160 }
161
162 impl<'a, 'b> Context<'a, 'b> {
163     /// Verifies one piece of a parse string. All errors are not emitted as
164     /// fatal so we can continue giving errors about this and possibly other
165     /// format strings.
166     fn verify_piece(&mut self, p: &parse::Piece) {
167         match *p {
168             parse::String(..) => {}
169             parse::NextArgument(ref arg) => {
170                 // width/precision first, if they have implicit positional
171                 // parameters it makes more sense to consume them first.
172                 self.verify_count(arg.format.width);
173                 self.verify_count(arg.format.precision);
174
175                 // argument second, if it's an implicit positional parameter
176                 // it's written second, so it should come after width/precision.
177                 let pos = match arg.position {
178                     parse::ArgumentNext => {
179                         let i = self.next_arg;
180                         if self.check_positional_ok() {
181                             self.next_arg += 1;
182                         }
183                         Exact(i)
184                     }
185                     parse::ArgumentIs(i) => Exact(i),
186                     parse::ArgumentNamed(s) => Named(s.to_string()),
187                 };
188
189                 let ty = Known(arg.format.ty.to_string());
190                 self.verify_arg_type(pos, ty);
191             }
192         }
193     }
194
195     fn verify_count(&mut self, c: parse::Count) {
196         match c {
197             parse::CountImplied | parse::CountIs(..) => {}
198             parse::CountIsParam(i) => {
199                 self.verify_arg_type(Exact(i), Unsigned);
200             }
201             parse::CountIsName(s) => {
202                 self.verify_arg_type(Named(s.to_string()), Unsigned);
203             }
204             parse::CountIsNextParam => {
205                 if self.check_positional_ok() {
206                     let next_arg = self.next_arg;
207                     self.verify_arg_type(Exact(next_arg), Unsigned);
208                     self.next_arg += 1;
209                 }
210             }
211         }
212     }
213
214     fn check_positional_ok(&mut self) -> bool {
215         if self.nest_level != 0 {
216             self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
217                                            arguments nested inside methods");
218             false
219         } else {
220             true
221         }
222     }
223
224     fn describe_num_args(&self) -> string::String {
225         match self.args.len() {
226             0 => "no arguments given".to_string(),
227             1 => "there is 1 argument".to_string(),
228             x => format!("there are {} arguments", x),
229         }
230     }
231
232     fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
233         match arg {
234             Exact(arg) => {
235                 if self.args.len() <= arg {
236                     let msg = format!("invalid reference to argument `{}` ({:s})",
237                                       arg, self.describe_num_args());
238
239                     self.ecx.span_err(self.fmtsp, msg.as_slice());
240                     return;
241                 }
242                 {
243                     let arg_type = match self.arg_types[arg] {
244                         None => None,
245                         Some(ref x) => Some(x)
246                     };
247                     self.verify_same(self.args[arg].span, &ty, arg_type);
248                 }
249                 if self.arg_types[arg].is_none() {
250                     *self.arg_types.get_mut(arg) = Some(ty);
251                 }
252             }
253
254             Named(name) => {
255                 let span = match self.names.find(&name) {
256                     Some(e) => e.span,
257                     None => {
258                         let msg = format!("there is no argument named `{}`", name);
259                         self.ecx.span_err(self.fmtsp, msg.as_slice());
260                         return;
261                     }
262                 };
263                 self.verify_same(span, &ty, self.name_types.find(&name));
264                 if !self.name_types.contains_key(&name) {
265                     self.name_types.insert(name.clone(), ty);
266                 }
267                 // Assign this named argument a slot in the arguments array if
268                 // it hasn't already been assigned a slot.
269                 if !self.name_positions.contains_key(&name) {
270                     let slot = self.name_positions.len();
271                     self.name_positions.insert(name, slot);
272                 }
273             }
274         }
275     }
276
277     /// When we're keeping track of the types that are declared for certain
278     /// arguments, we assume that `None` means we haven't seen this argument
279     /// yet, `Some(None)` means that we've seen the argument, but no format was
280     /// specified, and `Some(Some(x))` means that the argument was declared to
281     /// have type `x`.
282     ///
283     /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
284     /// that: `Some(None) == Some(Some(x))`
285     fn verify_same(&self,
286                    sp: Span,
287                    ty: &ArgumentType,
288                    before: Option<&ArgumentType>) {
289         let cur = match before {
290             None => return,
291             Some(t) => t,
292         };
293         if *ty == *cur {
294             return
295         }
296         match (cur, ty) {
297             (&Known(ref cur), &Known(ref ty)) => {
298                 self.ecx.span_err(sp,
299                                   format!("argument redeclared with type `{}` when \
300                                            it was previously `{}`",
301                                           *ty,
302                                           *cur).as_slice());
303             }
304             (&Known(ref cur), _) => {
305                 self.ecx.span_err(sp,
306                                   format!("argument used to format with `{}` was \
307                                            attempted to not be used for formatting",
308                                            *cur).as_slice());
309             }
310             (_, &Known(ref ty)) => {
311                 self.ecx.span_err(sp,
312                                   format!("argument previously used as a format \
313                                            argument attempted to be used as `{}`",
314                                            *ty).as_slice());
315             }
316             (_, _) => {
317                 self.ecx.span_err(sp, "argument declared with multiple formats");
318             }
319         }
320     }
321
322     /// These attributes are applied to all statics that this syntax extension
323     /// will generate.
324     fn static_attrs(ecx: &ExtCtxt, fmtsp: Span) -> Vec<ast::Attribute> {
325         // Flag statics as `inline` so LLVM can merge duplicate globals as much
326         // as possible (which we're generating a whole lot of).
327         let unnamed = ecx.meta_word(fmtsp, InternedString::new("inline"));
328         let unnamed = ecx.attribute(fmtsp, unnamed);
329
330         // Do not warn format string as dead code
331         let dead_code = ecx.meta_word(fmtsp, InternedString::new("dead_code"));
332         let allow_dead_code = ecx.meta_list(fmtsp,
333                                             InternedString::new("allow"),
334                                             vec![dead_code]);
335         let allow_dead_code = ecx.attribute(fmtsp, allow_dead_code);
336         vec![unnamed, allow_dead_code]
337     }
338
339     fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
340         vec![ecx.ident_of("std"), ecx.ident_of("fmt"), ecx.ident_of("rt"), ecx.ident_of(s)]
341     }
342
343     fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
344         let sp = self.fmtsp;
345         match c {
346             parse::CountIs(i) => {
347                 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIs"),
348                                           vec!(self.ecx.expr_uint(sp, i)))
349             }
350             parse::CountIsParam(i) => {
351                 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIsParam"),
352                                           vec!(self.ecx.expr_uint(sp, i)))
353             }
354             parse::CountImplied => {
355                 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
356                                                                     "CountImplied"));
357                 self.ecx.expr_path(path)
358             }
359             parse::CountIsNextParam => {
360                 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
361                                                                     "CountIsNextParam"));
362                 self.ecx.expr_path(path)
363             }
364             parse::CountIsName(n) => {
365                 let i = match self.name_positions.find_equiv(&n) {
366                     Some(&i) => i,
367                     None => 0, // error already emitted elsewhere
368                 };
369                 let i = i + self.args.len();
370                 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIsParam"),
371                                           vec!(self.ecx.expr_uint(sp, i)))
372             }
373         }
374     }
375
376     /// Translate the accumulated string literals to a literal expression
377     fn trans_literal_string(&mut self) -> P<ast::Expr> {
378         let sp = self.fmtsp;
379         let s = token::intern_and_get_ident(self.literal.as_slice());
380         self.literal.clear();
381         self.ecx.expr_str(sp, s)
382     }
383
384     /// Translate a `parse::Piece` to a static `rt::Argument` or append
385     /// to the `literal` string.
386     fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> {
387         let sp = self.fmtsp;
388         match *piece {
389             parse::String(s) => {
390                 self.literal.push_str(s);
391                 None
392             }
393             parse::NextArgument(ref arg) => {
394                 // Translate the position
395                 let pos = match arg.position {
396                     // These two have a direct mapping
397                     parse::ArgumentNext => {
398                         let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
399                                                                             "ArgumentNext"));
400                         self.ecx.expr_path(path)
401                     }
402                     parse::ArgumentIs(i) => {
403                         self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "ArgumentIs"),
404                                                   vec!(self.ecx.expr_uint(sp, i)))
405                     }
406                     // Named arguments are converted to positional arguments at
407                     // the end of the list of arguments
408                     parse::ArgumentNamed(n) => {
409                         let i = match self.name_positions.find_equiv(&n) {
410                             Some(&i) => i,
411                             None => 0, // error already emitted elsewhere
412                         };
413                         let i = i + self.args.len();
414                         self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "ArgumentIs"),
415                                                   vec!(self.ecx.expr_uint(sp, i)))
416                     }
417                 };
418
419                 let simple_arg = parse::Argument {
420                     position: parse::ArgumentNext,
421                     format: parse::FormatSpec {
422                         fill: arg.format.fill,
423                         align: parse::AlignUnknown,
424                         flags: 0,
425                         precision: parse::CountImplied,
426                         width: parse::CountImplied,
427                         ty: arg.format.ty
428                     }
429                 };
430
431                 let fill = match arg.format.fill { Some(c) => c, None => ' ' };
432
433                 if *arg != simple_arg || fill != ' ' {
434                     self.all_pieces_simple = false;
435                 }
436
437                 // Translate the format
438                 let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
439                 let align = match arg.format.align {
440                     parse::AlignLeft => {
441                         self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignLeft"))
442                     }
443                     parse::AlignRight => {
444                         self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignRight"))
445                     }
446                     parse::AlignCenter => {
447                         self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignCenter"))
448                     }
449                     parse::AlignUnknown => {
450                         self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignUnknown"))
451                     }
452                 };
453                 let align = self.ecx.expr_path(align);
454                 let flags = self.ecx.expr_uint(sp, arg.format.flags);
455                 let prec = self.trans_count(arg.format.precision);
456                 let width = self.trans_count(arg.format.width);
457                 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
458                 let fmt = self.ecx.expr_struct(sp, path, vec!(
459                     self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
460                     self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
461                     self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
462                     self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
463                     self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
464
465                 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument"));
466                 Some(self.ecx.expr_struct(sp, path, vec!(
467                     self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
468                     self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))))
469             }
470         }
471     }
472
473     fn item_static_array(ecx: &mut ExtCtxt,
474                          name: ast::Ident,
475                          piece_ty: P<ast::Ty>,
476                          pieces: Vec<P<ast::Expr>>)
477                          -> P<ast::Stmt> {
478         let fmtsp = piece_ty.span;
479         let pieces_len = ecx.expr_uint(fmtsp, pieces.len());
480         let fmt = ecx.expr_vec(fmtsp, pieces);
481         let ty = ast::TyFixedLengthVec(
482             piece_ty,
483             pieces_len
484         );
485         let ty = ecx.ty(fmtsp, ty);
486         let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
487         let item = ecx.item(fmtsp, name, Context::static_attrs(ecx, fmtsp), st);
488         let decl = respan(fmtsp, ast::DeclItem(item));
489         P(respan(fmtsp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
490     }
491
492     /// Actually builds the expression which the iformat! block will be expanded
493     /// to
494     fn to_expr(mut self, invocation: Invocation) -> P<ast::Expr> {
495         let mut lets = Vec::new();
496         let mut locals = Vec::new();
497         let mut names = Vec::from_fn(self.name_positions.len(), |_| None);
498         let mut pats = Vec::new();
499         let mut heads = Vec::new();
500
501         // First, declare all of our methods that are statics
502         for method in self.method_statics.into_iter() {
503             let decl = respan(self.fmtsp, ast::DeclItem(method));
504             lets.push(P(respan(self.fmtsp,
505                                ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))));
506         }
507
508         // Next, build up the static array which will become our precompiled
509         // format "string"
510         let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
511         let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name);
512         let piece_ty = self.ecx.ty_rptr(
513                 self.fmtsp,
514                 self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
515                 Some(static_lifetime),
516                 ast::MutImmutable);
517         lets.push(Context::item_static_array(self.ecx,
518                                              static_str_name,
519                                              piece_ty,
520                                              self.str_pieces));
521
522         // Then, build up the static array which will store our precompiled
523         // nonstandard placeholders, if there are any.
524         let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
525         if !self.all_pieces_simple {
526             let piece_ty = self.ecx.ty_path(self.ecx.path_all(
527                     self.fmtsp,
528                     true, Context::rtpath(self.ecx, "Argument"),
529                     vec![static_lifetime],
530                     vec![]
531                 ), None);
532             lets.push(Context::item_static_array(self.ecx,
533                                                  static_args_name,
534                                                  piece_ty,
535                                                  self.pieces));
536         }
537
538         // Right now there is a bug such that for the expression:
539         //      foo(bar(&1))
540         // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
541         // valid for the call to `foo`. To work around this all arguments to the
542         // format! string are shoved into locals. Furthermore, we shove the address
543         // of each variable because we don't want to move out of the arguments
544         // passed to this function.
545         for (i, e) in self.args.into_iter().enumerate() {
546             let arg_ty = match self.arg_types[i].as_ref() {
547                 Some(ty) => ty,
548                 None => continue // error already generated
549             };
550
551             let name = self.ecx.ident_of(format!("__arg{}", i).as_slice());
552             pats.push(self.ecx.pat_ident(e.span, name));
553             locals.push(Context::format_arg(self.ecx, e.span, arg_ty,
554                                             self.ecx.expr_ident(e.span, name)));
555             heads.push(self.ecx.expr_addr_of(e.span, e));
556         }
557         for name in self.name_ordering.iter() {
558             let e = match self.names.pop(name) {
559                 Some(e) => e,
560                 None => continue
561             };
562             let arg_ty = match self.name_types.find(name) {
563                 Some(ty) => ty,
564                 None => continue
565             };
566
567             let lname = self.ecx.ident_of(format!("__arg{}",
568                                                   *name).as_slice());
569             pats.push(self.ecx.pat_ident(e.span, lname));
570             *names.get_mut(self.name_positions[*name]) =
571                 Some(Context::format_arg(self.ecx, e.span, arg_ty,
572                                          self.ecx.expr_ident(e.span, lname)));
573             heads.push(self.ecx.expr_addr_of(e.span, e));
574         }
575
576         // Now create a vector containing all the arguments
577         let slicename = self.ecx.ident_of("__args_vec");
578         {
579             let args = names.into_iter().map(|a| a.unwrap());
580             let mut args = locals.into_iter().chain(args);
581             let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
582             lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
583         }
584
585         // Now create the fmt::Arguments struct with all our locals we created.
586         let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
587         let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
588
589         let (fn_name, fn_args) = if self.all_pieces_simple {
590             ("new", vec![pieces, args_slice])
591         } else {
592             let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
593             ("with_placeholders", vec![pieces, fmt, args_slice])
594         };
595
596         let result = self.ecx.expr_call_global(self.fmtsp, vec!(
597                 self.ecx.ident_of("std"),
598                 self.ecx.ident_of("fmt"),
599                 self.ecx.ident_of("Arguments"),
600                 self.ecx.ident_of(fn_name)), fn_args);
601
602         // We did all the work of making sure that the arguments
603         // structure is safe, so we can safely have an unsafe block.
604         let result = self.ecx.expr_block(P(ast::Block {
605            view_items: Vec::new(),
606            stmts: Vec::new(),
607            expr: Some(result),
608            id: ast::DUMMY_NODE_ID,
609            rules: ast::UnsafeBlock(ast::CompilerGenerated),
610            span: self.fmtsp,
611         }));
612         let resname = self.ecx.ident_of("__args");
613         lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
614         let res = self.ecx.expr_ident(self.fmtsp, resname);
615         let result = match invocation {
616             Call(e) => {
617                 let span = e.span;
618                 self.ecx.expr_call(span, e,
619                                    vec!(self.ecx.expr_addr_of(span, res)))
620             }
621             MethodCall(e, m) => {
622                 let span = e.span;
623                 self.ecx.expr_method_call(span, e, m,
624                                           vec!(self.ecx.expr_addr_of(span, res)))
625             }
626         };
627         let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
628                                                       Some(result)));
629
630         // Constructs an AST equivalent to:
631         //
632         //      match (&arg0, &arg1) {
633         //          (tmp0, tmp1) => body
634         //      }
635         //
636         // It was:
637         //
638         //      let tmp0 = &arg0;
639         //      let tmp1 = &arg1;
640         //      body
641         //
642         // Because of #11585 the new temporary lifetime rule, the enclosing
643         // statements for these temporaries become the let's themselves.
644         // If one or more of them are RefCell's, RefCell borrow() will also
645         // end there; they don't last long enough for body to use them. The
646         // match expression solves the scope problem.
647         //
648         // Note, it may also very well be transformed to:
649         //
650         //      match arg0 {
651         //          ref tmp0 => {
652         //              match arg1 => {
653         //                  ref tmp1 => body } } }
654         //
655         // But the nested match expression is proved to perform not as well
656         // as series of let's; the first approach does.
657         let pat = self.ecx.pat(self.fmtsp, ast::PatTup(pats));
658         let arm = self.ecx.arm(self.fmtsp, vec!(pat), body);
659         let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
660         self.ecx.expr_match(self.fmtsp, head, vec!(arm))
661     }
662
663     fn format_arg(ecx: &ExtCtxt, sp: Span,
664                   ty: &ArgumentType, arg: P<ast::Expr>)
665                   -> P<ast::Expr> {
666         let (krate, fmt_fn) = match *ty {
667             Known(ref tyname) => {
668                 match tyname.as_slice() {
669                     ""  => ("std", "secret_show"),
670                     "b" => ("std", "secret_bool"),
671                     "c" => ("std", "secret_char"),
672                     "d" | "i" => ("std", "secret_signed"),
673                     "e" => ("std", "secret_lower_exp"),
674                     "E" => ("std", "secret_upper_exp"),
675                     "f" => ("std", "secret_float"),
676                     "o" => ("std", "secret_octal"),
677                     "p" => ("std", "secret_pointer"),
678                     "s" => ("std", "secret_string"),
679                     "t" => ("std", "secret_binary"),
680                     "u" => ("std", "secret_unsigned"),
681                     "x" => ("std", "secret_lower_hex"),
682                     "X" => ("std", "secret_upper_hex"),
683                     _ => {
684                         ecx.span_err(sp,
685                                      format!("unknown format trait `{}`",
686                                              *tyname).as_slice());
687                         ("std", "dummy")
688                     }
689                 }
690             }
691             Unsigned => {
692                 return ecx.expr_call_global(sp, vec![
693                         ecx.ident_of("std"),
694                         ecx.ident_of("fmt"),
695                         ecx.ident_of("argumentuint")], vec![arg])
696             }
697         };
698
699         let format_fn = ecx.path_global(sp, vec![
700                 ecx.ident_of(krate),
701                 ecx.ident_of("fmt"),
702                 ecx.ident_of(fmt_fn)]);
703         ecx.expr_call_global(sp, vec![
704                 ecx.ident_of("std"),
705                 ecx.ident_of("fmt"),
706                 ecx.ident_of("argument")], vec![ecx.expr_path(format_fn), arg])
707     }
708 }
709
710 pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
711                                tts: &[ast::TokenTree])
712                                -> Box<base::MacResult+'cx> {
713
714     match parse_args(ecx, sp, false, tts) {
715         (invocation, Some((efmt, args, order, names))) => {
716             MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
717                                                       args, order, names))
718         }
719         (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
720     }
721 }
722
723 pub fn expand_format_args_method<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
724                                       tts: &[ast::TokenTree]) -> Box<base::MacResult+'cx> {
725
726     match parse_args(ecx, sp, true, tts) {
727         (invocation, Some((efmt, args, order, names))) => {
728             MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
729                                                       args, order, names))
730         }
731         (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
732     }
733 }
734
735 /// Take the various parts of `format_args!(extra, efmt, args...,
736 /// name=names...)` and construct the appropriate formatting
737 /// expression.
738 pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
739                                     invocation: Invocation,
740                                     efmt: P<ast::Expr>,
741                                     args: Vec<P<ast::Expr>>,
742                                     name_ordering: Vec<string::String>,
743                                     names: HashMap<string::String, P<ast::Expr>>)
744                                     -> P<ast::Expr> {
745     let arg_types = Vec::from_fn(args.len(), |_| None);
746     let mut cx = Context {
747         ecx: ecx,
748         args: args,
749         arg_types: arg_types,
750         names: names,
751         name_positions: HashMap::new(),
752         name_types: HashMap::new(),
753         name_ordering: name_ordering,
754         nest_level: 0,
755         next_arg: 0,
756         literal: string::String::new(),
757         pieces: Vec::new(),
758         str_pieces: Vec::new(),
759         all_pieces_simple: true,
760         method_statics: Vec::new(),
761         fmtsp: sp,
762     };
763     cx.fmtsp = efmt.span;
764     let fmt = match expr_to_string(cx.ecx,
765                                 efmt,
766                                 "format argument must be a string literal.") {
767         Some((fmt, _)) => fmt,
768         None => return DummyResult::raw_expr(sp)
769     };
770
771     let mut parser = parse::Parser::new(fmt.get());
772     loop {
773         match parser.next() {
774             Some(piece) => {
775                 if parser.errors.len() > 0 { break }
776                 cx.verify_piece(&piece);
777                 match cx.trans_piece(&piece) {
778                     Some(piece) => {
779                         let s = cx.trans_literal_string();
780                         cx.str_pieces.push(s);
781                         cx.pieces.push(piece);
782                     }
783                     None => {}
784                 }
785             }
786             None => break
787         }
788     }
789     match parser.errors.remove(0) {
790         Some(error) => {
791             cx.ecx.span_err(cx.fmtsp,
792                             format!("invalid format string: {}",
793                                     error).as_slice());
794             return DummyResult::raw_expr(sp);
795         }
796         None => {}
797     }
798     if !cx.literal.is_empty() {
799         let s = cx.trans_literal_string();
800         cx.str_pieces.push(s);
801     }
802
803     // Make sure that all arguments were used and all arguments have types.
804     for (i, ty) in cx.arg_types.iter().enumerate() {
805         if ty.is_none() {
806             cx.ecx.span_err(cx.args[i].span, "argument never used");
807         }
808     }
809     for (name, e) in cx.names.iter() {
810         if !cx.name_types.contains_key(name) {
811             cx.ecx.span_err(e.span, "named argument never used");
812         }
813     }
814
815     cx.to_expr(invocation)
816 }