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