]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/format.rs
Auto merge of #38867 - alexcrichton:ignore-test-on-windows, r=brson
[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 self::ArgumentType::*;
12 use self::Position::*;
13
14 use fmt_macros as parse;
15
16 use syntax::ast;
17 use syntax::ext::base::*;
18 use syntax::ext::base;
19 use syntax::ext::build::AstBuilder;
20 use syntax::parse::token;
21 use syntax::ptr::P;
22 use syntax::symbol::{Symbol, keywords};
23 use syntax_pos::{Span, DUMMY_SP};
24 use syntax::tokenstream;
25
26 use std::collections::{HashMap, HashSet};
27 use std::collections::hash_map::Entry;
28
29 #[derive(PartialEq)]
30 enum ArgumentType {
31     Placeholder(String),
32     Count,
33 }
34
35 enum Position {
36     Exact(usize),
37     Named(String),
38 }
39
40 struct Context<'a, 'b: 'a> {
41     ecx: &'a mut ExtCtxt<'b>,
42     /// The macro's call site. References to unstable formatting internals must
43     /// use this span to pass the stability checker.
44     macsp: Span,
45     /// The span of the format string literal.
46     fmtsp: Span,
47
48     /// List of parsed argument expressions.
49     /// Named expressions are resolved early, and are appended to the end of
50     /// argument expressions.
51     ///
52     /// Example showing the various data structures in motion:
53     ///
54     /// * Original: `"{foo:o} {:o} {foo:x} {0:x} {1:o} {:x} {1:x} {0:o}"`
55     /// * Implicit argument resolution: `"{foo:o} {0:o} {foo:x} {0:x} {1:o} {1:x} {1:x} {0:o}"`
56     /// * Name resolution: `"{2:o} {0:o} {2:x} {0:x} {1:o} {1:x} {1:x} {0:o}"`
57     /// * `arg_types` (in JSON): `[[0, 1, 0], [0, 1, 1], [0, 1]]`
58     /// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]`
59     /// * `names` (in JSON): `{"foo": 2}`
60     args: Vec<P<ast::Expr>>,
61     /// Placeholder slot numbers indexed by argument.
62     arg_types: Vec<Vec<usize>>,
63     /// Unique format specs seen for each argument.
64     arg_unique_types: Vec<Vec<ArgumentType>>,
65     /// Map from named arguments to their resolved indices.
66     names: HashMap<String, usize>,
67
68     /// The latest consecutive literal strings, or empty if there weren't any.
69     literal: String,
70
71     /// Collection of the compiled `rt::Argument` structures
72     pieces: Vec<P<ast::Expr>>,
73     /// Collection of string literals
74     str_pieces: Vec<P<ast::Expr>>,
75     /// Stays `true` if all formatting parameters are default (as in "{}{}").
76     all_pieces_simple: bool,
77
78     /// Mapping between positional argument references and indices into the
79     /// final generated static argument array. We record the starting indices
80     /// corresponding to each positional argument, and number of references
81     /// consumed so far for each argument, to facilitate correct `Position`
82     /// mapping in `trans_piece`. In effect this can be seen as a "flattened"
83     /// version of `arg_unique_types`.
84     ///
85     /// Again with the example described above in docstring for `args`:
86     ///
87     /// * `arg_index_map` (in JSON): `[[0, 1, 0], [2, 3, 3], [4, 5]]`
88     arg_index_map: Vec<Vec<usize>>,
89
90     /// Starting offset of count argument slots.
91     count_args_index_offset: usize,
92
93     /// Count argument slots and tracking data structures.
94     /// Count arguments are separately tracked for de-duplication in case
95     /// multiple references are made to one argument. For example, in this
96     /// format string:
97     ///
98     /// * Original: `"{:.*} {:.foo$} {1:.*} {:.0$}"`
99     /// * Implicit argument resolution: `"{1:.0$} {2:.foo$} {1:.3$} {4:.0$}"`
100     /// * Name resolution: `"{1:.0$} {2:.5$} {1:.3$} {4:.0$}"`
101     /// * `count_positions` (in JSON): `{0: 0, 5: 1, 3: 2}`
102     /// * `count_args`: `vec![Exact(0), Exact(5), Exact(3)]`
103     count_args: Vec<Position>,
104     /// Relative slot numbers for count arguments.
105     count_positions: HashMap<usize, usize>,
106     /// Number of count slots assigned.
107     count_positions_count: usize,
108
109     /// Current position of the implicit positional arg pointer, as if it
110     /// still existed in this phase of processing.
111     /// Used only for `all_pieces_simple` tracking in `trans_piece`.
112     curarg: usize,
113 }
114
115 /// Parses the arguments from the given list of tokens, returning None
116 /// if there's a parse error so we can continue parsing other format!
117 /// expressions.
118 ///
119 /// If parsing succeeds, the return value is:
120 /// ```ignore
121 /// Some((fmtstr, parsed arguments, index map for named arguments))
122 /// ```
123 fn parse_args(ecx: &mut ExtCtxt,
124               sp: Span,
125               tts: &[tokenstream::TokenTree])
126               -> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, HashMap<String, usize>)> {
127     let mut args = Vec::<P<ast::Expr>>::new();
128     let mut names = HashMap::<String, usize>::new();
129
130     let mut p = ecx.new_parser_from_tts(tts);
131
132     if p.token == token::Eof {
133         ecx.span_err(sp, "requires at least a format string argument");
134         return None;
135     }
136     let fmtstr = panictry!(p.parse_expr());
137     let mut named = false;
138     while p.token != token::Eof {
139         if !p.eat(&token::Comma) {
140             ecx.span_err(sp, "expected token: `,`");
141             return None;
142         }
143         if p.token == token::Eof {
144             break;
145         } // accept trailing commas
146         if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
147             named = true;
148             let ident = match p.token {
149                 token::Ident(i) => {
150                     p.bump();
151                     i
152                 }
153                 _ if named => {
154                     ecx.span_err(p.span,
155                                  "expected ident, positional arguments \
156                                  cannot follow named arguments");
157                     return None;
158                 }
159                 _ => {
160                     ecx.span_err(p.span,
161                                  &format!("expected ident for named argument, found `{}`",
162                                           p.this_token_to_string()));
163                     return None;
164                 }
165             };
166             let name: &str = &ident.name.as_str();
167
168             panictry!(p.expect(&token::Eq));
169             let e = panictry!(p.parse_expr());
170             if let Some(prev) = names.get(name) {
171                 ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", name))
172                     .span_note(args[*prev].span, "previously here")
173                     .emit();
174                 continue;
175             }
176
177             // Resolve names into slots early.
178             // Since all the positional args are already seen at this point
179             // if the input is valid, we can simply append to the positional
180             // args. And remember the names.
181             let slot = args.len();
182             names.insert(name.to_string(), slot);
183             args.push(e);
184         } else {
185             args.push(panictry!(p.parse_expr()));
186         }
187     }
188     Some((fmtstr, args, names))
189 }
190
191 impl<'a, 'b> Context<'a, 'b> {
192     fn resolve_name_inplace(&self, p: &mut parse::Piece) {
193         // NOTE: the `unwrap_or` branch is needed in case of invalid format
194         // arguments, e.g. `format_args!("{foo}")`.
195         let lookup = |s| *self.names.get(s).unwrap_or(&0);
196
197         match *p {
198             parse::String(_) => {}
199             parse::NextArgument(ref mut arg) => {
200                 if let parse::ArgumentNamed(s) = arg.position {
201                     arg.position = parse::ArgumentIs(lookup(s));
202                 }
203                 if let parse::CountIsName(s) = arg.format.width {
204                     arg.format.width = parse::CountIsParam(lookup(s));
205                 }
206                 if let parse::CountIsName(s) = arg.format.precision {
207                     arg.format.precision = parse::CountIsParam(lookup(s));
208                 }
209             }
210         }
211     }
212
213     /// Verifies one piece of a parse string, and remembers it if valid.
214     /// All errors are not emitted as fatal so we can continue giving errors
215     /// about this and possibly other format strings.
216     fn verify_piece(&mut self, p: &parse::Piece) {
217         match *p {
218             parse::String(..) => {}
219             parse::NextArgument(ref arg) => {
220                 // width/precision first, if they have implicit positional
221                 // parameters it makes more sense to consume them first.
222                 self.verify_count(arg.format.width);
223                 self.verify_count(arg.format.precision);
224
225                 // argument second, if it's an implicit positional parameter
226                 // it's written second, so it should come after width/precision.
227                 let pos = match arg.position {
228                     parse::ArgumentIs(i) => Exact(i),
229                     parse::ArgumentNamed(s) => Named(s.to_string()),
230                 };
231
232                 let ty = Placeholder(arg.format.ty.to_string());
233                 self.verify_arg_type(pos, ty);
234             }
235         }
236     }
237
238     fn verify_count(&mut self, c: parse::Count) {
239         match c {
240             parse::CountImplied |
241             parse::CountIs(..) => {}
242             parse::CountIsParam(i) => {
243                 self.verify_arg_type(Exact(i), Count);
244             }
245             parse::CountIsName(s) => {
246                 self.verify_arg_type(Named(s.to_string()), Count);
247             }
248         }
249     }
250
251     fn describe_num_args(&self) -> String {
252         match self.args.len() {
253             0 => "no arguments given".to_string(),
254             1 => "there is 1 argument".to_string(),
255             x => format!("there are {} arguments", x),
256         }
257     }
258
259     /// Actually verifies and tracks a given format placeholder
260     /// (a.k.a. argument).
261     fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
262         match arg {
263             Exact(arg) => {
264                 if self.args.len() <= arg {
265                     let msg = format!("invalid reference to argument `{}` ({})",
266                                       arg,
267                                       self.describe_num_args());
268
269                     self.ecx.span_err(self.fmtsp, &msg[..]);
270                     return;
271                 }
272                 match ty {
273                     Placeholder(_) => {
274                         // record every (position, type) combination only once
275                         let ref mut seen_ty = self.arg_unique_types[arg];
276                         let i = match seen_ty.iter().position(|x| *x == ty) {
277                             Some(i) => i,
278                             None => {
279                                 let i = seen_ty.len();
280                                 seen_ty.push(ty);
281                                 i
282                             }
283                         };
284                         self.arg_types[arg].push(i);
285                     }
286                     Count => {
287                         match self.count_positions.entry(arg) {
288                             Entry::Vacant(e) => {
289                                 let i = self.count_positions_count;
290                                 e.insert(i);
291                                 self.count_args.push(Exact(arg));
292                                 self.count_positions_count += 1;
293                             }
294                             Entry::Occupied(_) => {}
295                         }
296                     }
297                 }
298             }
299
300             Named(name) => {
301                 let idx = match self.names.get(&name) {
302                     Some(e) => *e,
303                     None => {
304                         let msg = format!("there is no argument named `{}`", name);
305                         self.ecx.span_err(self.fmtsp, &msg[..]);
306                         return;
307                     }
308                 };
309                 // Treat as positional arg.
310                 self.verify_arg_type(Exact(idx), ty)
311             }
312         }
313     }
314
315     /// Builds the mapping between format placeholders and argument objects.
316     fn build_index_map(&mut self) {
317         // NOTE: Keep the ordering the same as `into_expr`'s expansion would do!
318         let args_len = self.args.len();
319         self.arg_index_map.reserve(args_len);
320
321         let mut sofar = 0usize;
322
323         // Map the arguments
324         for i in 0..args_len {
325             let ref arg_types = self.arg_types[i];
326             let mut arg_offsets = Vec::with_capacity(arg_types.len());
327             for offset in arg_types {
328                 arg_offsets.push(sofar + *offset);
329             }
330             self.arg_index_map.push(arg_offsets);
331             sofar += self.arg_unique_types[i].len();
332         }
333
334         // Record starting index for counts, which appear just after arguments
335         self.count_args_index_offset = sofar;
336     }
337
338     fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
339         ecx.std_path(&["fmt", "rt", "v1", s])
340     }
341
342     fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
343         let sp = self.macsp;
344         let count = |c, arg| {
345             let mut path = Context::rtpath(self.ecx, "Count");
346             path.push(self.ecx.ident_of(c));
347             match arg {
348                 Some(arg) => self.ecx.expr_call_global(sp, path, vec![arg]),
349                 None => self.ecx.expr_path(self.ecx.path_global(sp, path)),
350             }
351         };
352         match c {
353             parse::CountIs(i) => count("Is", Some(self.ecx.expr_usize(sp, i))),
354             parse::CountIsParam(i) => {
355                 // This needs mapping too, as `i` is referring to a macro
356                 // argument.
357                 let i = match self.count_positions.get(&i) {
358                     Some(&i) => i,
359                     None => 0, // error already emitted elsewhere
360                 };
361                 let i = i + self.count_args_index_offset;
362                 count("Param", Some(self.ecx.expr_usize(sp, i)))
363             }
364             parse::CountImplied => count("Implied", None),
365             // should never be the case, names are already resolved
366             parse::CountIsName(_) => panic!("should never happen"),
367         }
368     }
369
370     /// Translate the accumulated string literals to a literal expression
371     fn trans_literal_string(&mut self) -> P<ast::Expr> {
372         let sp = self.fmtsp;
373         let s = Symbol::intern(&self.literal);
374         self.literal.clear();
375         self.ecx.expr_str(sp, s)
376     }
377
378     /// Translate a `parse::Piece` to a static `rt::Argument` or append
379     /// to the `literal` string.
380     fn trans_piece(&mut self,
381                    piece: &parse::Piece,
382                    arg_index_consumed: &mut Vec<usize>)
383                    -> Option<P<ast::Expr>> {
384         let sp = self.macsp;
385         match *piece {
386             parse::String(s) => {
387                 self.literal.push_str(s);
388                 None
389             }
390             parse::NextArgument(ref arg) => {
391                 // Translate the position
392                 let pos = {
393                     let pos = |c, arg| {
394                         let mut path = Context::rtpath(self.ecx, "Position");
395                         path.push(self.ecx.ident_of(c));
396                         match arg {
397                             Some(i) => {
398                                 let arg = self.ecx.expr_usize(sp, i);
399                                 self.ecx.expr_call_global(sp, path, vec![arg])
400                             }
401                             None => self.ecx.expr_path(self.ecx.path_global(sp, path)),
402                         }
403                     };
404                     match arg.position {
405                         parse::ArgumentIs(i) => {
406                             // Map to index in final generated argument array
407                             // in case of multiple types specified
408                             let arg_idx = match arg_index_consumed.get_mut(i) {
409                                 None => 0, // error already emitted elsewhere
410                                 Some(offset) => {
411                                     let ref idx_map = self.arg_index_map[i];
412                                     // unwrap_or branch: error already emitted elsewhere
413                                     let arg_idx = *idx_map.get(*offset).unwrap_or(&0);
414                                     *offset += 1;
415                                     arg_idx
416                                 }
417                             };
418                             pos("At", Some(arg_idx))
419                         }
420
421                         // should never be the case, because names are already
422                         // resolved.
423                         parse::ArgumentNamed(_) => panic!("should never happen"),
424                     }
425                 };
426
427                 let simple_arg = parse::Argument {
428                     position: {
429                         // We don't have ArgumentNext any more, so we have to
430                         // track the current argument ourselves.
431                         let i = self.curarg;
432                         self.curarg += 1;
433                         parse::ArgumentIs(i)
434                     },
435                     format: parse::FormatSpec {
436                         fill: arg.format.fill,
437                         align: parse::AlignUnknown,
438                         flags: 0,
439                         precision: parse::CountImplied,
440                         width: parse::CountImplied,
441                         ty: arg.format.ty,
442                     },
443                 };
444
445                 let fill = match arg.format.fill {
446                     Some(c) => c,
447                     None => ' ',
448                 };
449
450                 if *arg != simple_arg || fill != ' ' {
451                     self.all_pieces_simple = false;
452                 }
453
454                 // Translate the format
455                 let fill = self.ecx.expr_lit(sp, ast::LitKind::Char(fill));
456                 let align = |name| {
457                     let mut p = Context::rtpath(self.ecx, "Alignment");
458                     p.push(self.ecx.ident_of(name));
459                     self.ecx.path_global(sp, p)
460                 };
461                 let align = match arg.format.align {
462                     parse::AlignLeft => align("Left"),
463                     parse::AlignRight => align("Right"),
464                     parse::AlignCenter => align("Center"),
465                     parse::AlignUnknown => align("Unknown"),
466                 };
467                 let align = self.ecx.expr_path(align);
468                 let flags = self.ecx.expr_u32(sp, arg.format.flags);
469                 let prec = self.trans_count(arg.format.precision);
470                 let width = self.trans_count(arg.format.width);
471                 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
472                 let fmt =
473                     self.ecx.expr_struct(sp,
474                                          path,
475                                          vec![self.ecx
476                                                   .field_imm(sp, self.ecx.ident_of("fill"), fill),
477                                               self.ecx.field_imm(sp,
478                                                                  self.ecx.ident_of("align"),
479                                                                  align),
480                                               self.ecx.field_imm(sp,
481                                                                  self.ecx.ident_of("flags"),
482                                                                  flags),
483                                               self.ecx.field_imm(sp,
484                                                                  self.ecx.ident_of("precision"),
485                                                                  prec),
486                                               self.ecx.field_imm(sp,
487                                                                  self.ecx.ident_of("width"),
488                                                                  width)]);
489
490                 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument"));
491                 Some(self.ecx.expr_struct(sp,
492                                           path,
493                                           vec![self.ecx.field_imm(sp,
494                                                                   self.ecx.ident_of("position"),
495                                                                   pos),
496                                                self.ecx.field_imm(sp,
497                                                                   self.ecx.ident_of("format"),
498                                                                   fmt)]))
499             }
500         }
501     }
502
503     fn static_array(ecx: &mut ExtCtxt,
504                     name: &str,
505                     piece_ty: P<ast::Ty>,
506                     pieces: Vec<P<ast::Expr>>)
507                     -> P<ast::Expr> {
508         let sp = piece_ty.span;
509         let ty = ecx.ty_rptr(sp,
510                              ecx.ty(sp, ast::TyKind::Slice(piece_ty)),
511                              Some(ecx.lifetime(sp, keywords::StaticLifetime.name())),
512                              ast::Mutability::Immutable);
513         let slice = ecx.expr_vec_slice(sp, pieces);
514         // static instead of const to speed up codegen by not requiring this to be inlined
515         let st = ast::ItemKind::Static(ty, ast::Mutability::Immutable, slice);
516
517         let name = ecx.ident_of(name);
518         let item = ecx.item(sp, name, vec![], st);
519         let stmt = ast::Stmt {
520             id: ast::DUMMY_NODE_ID,
521             node: ast::StmtKind::Item(item),
522             span: sp,
523         };
524
525         // Wrap the declaration in a block so that it forms a single expression.
526         ecx.expr_block(ecx.block(sp, vec![stmt, ecx.stmt_expr(ecx.expr_ident(sp, name))]))
527     }
528
529     /// Actually builds the expression which the format_args! block will be
530     /// expanded to
531     fn into_expr(mut self) -> P<ast::Expr> {
532         let mut locals = Vec::new();
533         let mut counts = Vec::new();
534         let mut pats = Vec::new();
535         let mut heads = Vec::new();
536
537         // First, build up the static array which will become our precompiled
538         // format "string"
539         let static_lifetime = self.ecx.lifetime(self.fmtsp, keywords::StaticLifetime.name());
540         let piece_ty = self.ecx.ty_rptr(self.fmtsp,
541                                         self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
542                                         Some(static_lifetime),
543                                         ast::Mutability::Immutable);
544         let pieces = Context::static_array(self.ecx, "__STATIC_FMTSTR", piece_ty, self.str_pieces);
545
546         // Before consuming the expressions, we have to remember spans for
547         // count arguments as they are now generated separate from other
548         // arguments, hence have no access to the `P<ast::Expr>`'s.
549         let spans_pos: Vec<_> = self.args.iter().map(|e| e.span.clone()).collect();
550
551         // Right now there is a bug such that for the expression:
552         //      foo(bar(&1))
553         // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
554         // valid for the call to `foo`. To work around this all arguments to the
555         // format! string are shoved into locals. Furthermore, we shove the address
556         // of each variable because we don't want to move out of the arguments
557         // passed to this function.
558         for (i, e) in self.args.into_iter().enumerate() {
559             let name = self.ecx.ident_of(&format!("__arg{}", i));
560             pats.push(self.ecx.pat_ident(DUMMY_SP, name));
561             for ref arg_ty in self.arg_unique_types[i].iter() {
562                 locals.push(Context::format_arg(self.ecx,
563                                                 self.macsp,
564                                                 e.span,
565                                                 arg_ty,
566                                                 self.ecx.expr_ident(e.span, name)));
567             }
568             heads.push(self.ecx.expr_addr_of(e.span, e));
569         }
570         for pos in self.count_args {
571             let name = self.ecx.ident_of(&match pos {
572                 Exact(i) => format!("__arg{}", i),
573                 _ => panic!("should never happen"),
574             });
575             let span = match pos {
576                 Exact(i) => spans_pos[i],
577                 _ => panic!("should never happen"),
578             };
579             counts.push(Context::format_arg(self.ecx,
580                                             self.macsp,
581                                             span,
582                                             &Count,
583                                             self.ecx.expr_ident(span, name)));
584         }
585
586         // Now create a vector containing all the arguments
587         let args = locals.into_iter().chain(counts.into_iter());
588
589         let args_array = self.ecx.expr_vec(self.fmtsp, args.collect());
590
591         // Constructs an AST equivalent to:
592         //
593         //      match (&arg0, &arg1) {
594         //          (tmp0, tmp1) => args_array
595         //      }
596         //
597         // It was:
598         //
599         //      let tmp0 = &arg0;
600         //      let tmp1 = &arg1;
601         //      args_array
602         //
603         // Because of #11585 the new temporary lifetime rule, the enclosing
604         // statements for these temporaries become the let's themselves.
605         // If one or more of them are RefCell's, RefCell borrow() will also
606         // end there; they don't last long enough for args_array to use them.
607         // The match expression solves the scope problem.
608         //
609         // Note, it may also very well be transformed to:
610         //
611         //      match arg0 {
612         //          ref tmp0 => {
613         //              match arg1 => {
614         //                  ref tmp1 => args_array } } }
615         //
616         // But the nested match expression is proved to perform not as well
617         // as series of let's; the first approach does.
618         let pat = self.ecx.pat_tuple(self.fmtsp, pats);
619         let arm = self.ecx.arm(self.fmtsp, vec![pat], args_array);
620         let head = self.ecx.expr(self.fmtsp, ast::ExprKind::Tup(heads));
621         let result = self.ecx.expr_match(self.fmtsp, head, vec![arm]);
622
623         let args_slice = self.ecx.expr_addr_of(self.fmtsp, result);
624
625         // Now create the fmt::Arguments struct with all our locals we created.
626         let (fn_name, fn_args) = if self.all_pieces_simple {
627             ("new_v1", vec![pieces, args_slice])
628         } else {
629             // Build up the static array which will store our precompiled
630             // nonstandard placeholders, if there are any.
631             let piece_ty = self.ecx
632                 .ty_path(self.ecx.path_global(self.macsp, Context::rtpath(self.ecx, "Argument")));
633             let fmt = Context::static_array(self.ecx, "__STATIC_FMTARGS", piece_ty, self.pieces);
634
635             ("new_v1_formatted", vec![pieces, args_slice, fmt])
636         };
637
638         let path = self.ecx.std_path(&["fmt", "Arguments", fn_name]);
639         self.ecx.expr_call_global(self.macsp, path, fn_args)
640     }
641
642     fn format_arg(ecx: &ExtCtxt,
643                   macsp: Span,
644                   sp: Span,
645                   ty: &ArgumentType,
646                   arg: P<ast::Expr>)
647                   -> P<ast::Expr> {
648         let trait_ = match *ty {
649             Placeholder(ref tyname) => {
650                 match &tyname[..] {
651                     "" => "Display",
652                     "?" => "Debug",
653                     "e" => "LowerExp",
654                     "E" => "UpperExp",
655                     "o" => "Octal",
656                     "p" => "Pointer",
657                     "b" => "Binary",
658                     "x" => "LowerHex",
659                     "X" => "UpperHex",
660                     _ => {
661                         ecx.span_err(sp, &format!("unknown format trait `{}`", *tyname));
662                         "Dummy"
663                     }
664                 }
665             }
666             Count => {
667                 let path = ecx.std_path(&["fmt", "ArgumentV1", "from_usize"]);
668                 return ecx.expr_call_global(macsp, path, vec![arg]);
669             }
670         };
671
672         let path = ecx.std_path(&["fmt", trait_, "fmt"]);
673         let format_fn = ecx.path_global(sp, path);
674         let path = ecx.std_path(&["fmt", "ArgumentV1", "new"]);
675         ecx.expr_call_global(macsp, path, vec![arg, ecx.expr_path(format_fn)])
676     }
677 }
678
679 pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt,
680                                sp: Span,
681                                tts: &[tokenstream::TokenTree])
682                                -> Box<base::MacResult + 'cx> {
683
684     match parse_args(ecx, sp, tts) {
685         Some((efmt, args, names)) => {
686             MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt, args, names))
687         }
688         None => DummyResult::expr(sp),
689     }
690 }
691
692 /// Take the various parts of `format_args!(efmt, args..., name=names...)`
693 /// and construct the appropriate formatting expression.
694 pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
695                                     sp: Span,
696                                     efmt: P<ast::Expr>,
697                                     args: Vec<P<ast::Expr>>,
698                                     names: HashMap<String, usize>)
699                                     -> P<ast::Expr> {
700     // NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because
701     // `ArgumentType` does not derive `Clone`.
702     let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
703     let arg_unique_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
704     let macsp = ecx.call_site();
705     let msg = "format argument must be a string literal.";
706     let fmt = match expr_to_spanned_string(ecx, efmt, msg) {
707         Some(fmt) => fmt,
708         None => return DummyResult::raw_expr(sp),
709     };
710
711     let mut cx = Context {
712         ecx: ecx,
713         args: args,
714         arg_types: arg_types,
715         arg_unique_types: arg_unique_types,
716         names: names,
717         curarg: 0,
718         arg_index_map: Vec::new(),
719         count_args: Vec::new(),
720         count_positions: HashMap::new(),
721         count_positions_count: 0,
722         count_args_index_offset: 0,
723         literal: String::new(),
724         pieces: Vec::new(),
725         str_pieces: Vec::new(),
726         all_pieces_simple: true,
727         macsp: macsp,
728         fmtsp: fmt.span,
729     };
730
731     let fmt_str = &*fmt.node.0.as_str();
732     let mut parser = parse::Parser::new(fmt_str);
733     let mut pieces = vec![];
734
735     loop {
736         match parser.next() {
737             Some(mut piece) => {
738                 if !parser.errors.is_empty() {
739                     break;
740                 }
741                 cx.verify_piece(&piece);
742                 cx.resolve_name_inplace(&mut piece);
743                 pieces.push(piece);
744             }
745             None => break,
746         }
747     }
748
749     cx.build_index_map();
750
751     let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()];
752     for piece in pieces {
753         if let Some(piece) = cx.trans_piece(&piece, &mut arg_index_consumed) {
754             let s = cx.trans_literal_string();
755             cx.str_pieces.push(s);
756             cx.pieces.push(piece);
757         }
758     }
759
760     if !parser.errors.is_empty() {
761         let (err, note) = parser.errors.remove(0);
762         let mut e = cx.ecx.struct_span_err(cx.fmtsp, &format!("invalid format string: {}", err));
763         if let Some(note) = note {
764             e.note(&note);
765         }
766         e.emit();
767         return DummyResult::raw_expr(sp);
768     }
769     if !cx.literal.is_empty() {
770         let s = cx.trans_literal_string();
771         cx.str_pieces.push(s);
772     }
773
774     // Make sure that all arguments were used and all arguments have types.
775     let num_pos_args = cx.args.len() - cx.names.len();
776     let mut errs = vec![];
777     for (i, ty) in cx.arg_types.iter().enumerate() {
778         if ty.len() == 0 {
779             if cx.count_positions.contains_key(&i) {
780                 continue;
781             }
782             let msg = if i >= num_pos_args {
783                 // named argument
784                 "named argument never used"
785             } else {
786                 // positional argument
787                 "argument never used"
788             };
789             errs.push((cx.args[i].span, msg));
790         }
791     }
792     if errs.len() > 0 {
793         let args_used = cx.arg_types.len() - errs.len();
794         let args_unused = errs.len();
795
796         let mut diag = {
797             if errs.len() == 1 {
798                 let (sp, msg) = errs.into_iter().next().unwrap();
799                 cx.ecx.struct_span_err(sp, msg)
800             } else {
801                 let mut diag = cx.ecx.struct_span_err(cx.fmtsp,
802                     "multiple unused formatting arguments");
803                 for (sp, msg) in errs {
804                     diag.span_note(sp, msg);
805                 }
806                 diag
807             }
808         };
809
810         // Decide if we want to look for foreign formatting directives.
811         if args_used < args_unused {
812             use super::format_foreign as foreign;
813
814             // The set of foreign substitutions we've explained.  This prevents spamming the user
815             // with `%d should be written as {}` over and over again.
816             let mut explained = HashSet::new();
817
818             // Used to ensure we only report translations for *one* kind of foreign format.
819             let mut found_foreign = false;
820
821             macro_rules! check_foreign {
822                 ($kind:ident) => {{
823                     let mut show_doc_note = false;
824
825                     for sub in foreign::$kind::iter_subs(fmt_str) {
826                         let trn = match sub.translate() {
827                             Some(trn) => trn,
828
829                             // If it has no translation, don't call it out specifically.
830                             None => continue,
831                         };
832
833                         let sub = String::from(sub.as_str());
834                         if explained.contains(&sub) {
835                             continue;
836                         }
837                         explained.insert(sub.clone());
838
839                         if !found_foreign {
840                             found_foreign = true;
841                             show_doc_note = true;
842                         }
843
844                         diag.help(&format!("`{}` should be written as `{}`", sub, trn));
845                     }
846
847                     if show_doc_note {
848                         diag.note(concat!(stringify!($kind), " formatting not supported; see \
849                                 the documentation for `std::fmt`"));
850                     }
851                 }};
852             }
853
854             check_foreign!(printf);
855             if !found_foreign {
856                 check_foreign!(shell);
857             }
858         }
859
860         diag.emit();
861     }
862
863     cx.into_expr()
864 }