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