1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 use codemap::{Span, respan};
16 use ext::build::AstBuilder;
17 use parse::token::InternedString;
21 use parse = fmt_macros;
22 use collections::{HashMap, HashSet};
36 struct Context<'a, 'b> {
37 ecx: &'a mut ExtCtxt<'b>,
40 // Parsed argument expressions and the types that we've found so far for
42 args: Vec<@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
48 names: HashMap<StrBuf, @ast::Expr>,
49 name_types: HashMap<StrBuf, ArgumentType>,
50 name_ordering: Vec<StrBuf>,
52 // Collection of the compiled `rt::Piece` structures
53 pieces: Vec<@ast::Expr> ,
54 name_positions: HashMap<StrBuf, uint>,
55 method_statics: Vec<@ast::Item> ,
57 // Updated as arguments are consumed or methods are entered
64 MethodCall(@ast::Expr, ast::Ident),
67 /// Parses the arguments from the given list of tokens, returning None
68 /// if there's a parse error so we can continue parsing other format!
71 /// If parsing succeeds, the second return value is:
73 /// Some((fmtstr, unnamed arguments, ordering of named arguments,
75 fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
76 tts: &[ast::TokenTree])
77 -> (Invocation, Option<(@ast::Expr, Vec<@ast::Expr>, Vec<StrBuf>,
78 HashMap<StrBuf, @ast::Expr>)>) {
79 let mut args = Vec::new();
80 let mut names = HashMap::<StrBuf, @ast::Expr>::new();
81 let mut order = Vec::new();
83 let mut p = rsparse::new_parser_from_tts(ecx.parse_sess(),
86 .map(|x| (*x).clone())
88 // Parse the leading function expression (maybe a block, maybe a path)
89 let invocation = if allow_method {
90 let e = p.parse_expr();
91 if !p.eat(&token::COMMA) {
92 ecx.span_err(sp, "expected token: `,`");
93 return (Call(e), None);
95 MethodCall(e, p.parse_ident())
99 if !p.eat(&token::COMMA) {
100 ecx.span_err(sp, "expected token: `,`");
101 return (invocation, None);
104 if p.token == token::EOF {
105 ecx.span_err(sp, "requires at least a format string argument");
106 return (invocation, None);
108 let fmtstr = p.parse_expr();
109 let mut named = false;
110 while p.token != token::EOF {
111 if !p.eat(&token::COMMA) {
112 ecx.span_err(sp, "expected token: `,`");
113 return (invocation, None);
115 if p.token == token::EOF { break } // accept trailing commas
116 if named || (token::is_ident(&p.token) &&
117 p.look_ahead(1, |t| *t == token::EQ)) {
119 let ident = match p.token {
120 token::IDENT(i, _) => {
126 "expected ident, positional arguments \
127 cannot follow named arguments");
128 return (invocation, None);
132 format!("expected ident for named argument, but found `{}`",
133 p.this_token_to_str()));
134 return (invocation, None);
137 let interned_name = token::get_ident(ident);
138 let name = interned_name.get();
139 p.expect(&token::EQ);
140 let e = p.parse_expr();
141 match names.find_equiv(&name) {
144 ecx.span_err(e.span, format!("duplicate argument named `{}`", name));
145 ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
149 order.push(name.to_strbuf());
150 names.insert(name.to_strbuf(), e);
152 args.push(p.parse_expr());
155 return (invocation, Some((fmtstr, args, order, names)));
158 impl<'a, 'b> Context<'a, 'b> {
159 /// Verifies one piece of a parse string. All errors are not emitted as
160 /// fatal so we can continue giving errors about this and possibly other
162 fn verify_piece(&mut self, p: &parse::Piece) {
164 parse::String(..) => {}
165 parse::CurrentArgument => {
166 if self.nest_level == 0 {
167 self.ecx.span_err(self.fmtsp,
168 "`#` reference used with nothing to \
172 parse::Argument(ref arg) => {
173 // width/precision first, if they have implicit positional
174 // parameters it makes more sense to consume them first.
175 self.verify_count(arg.format.width);
176 self.verify_count(arg.format.precision);
178 // argument second, if it's an implicit positional parameter
179 // it's written second, so it should come after width/precision.
180 let pos = match arg.position {
181 parse::ArgumentNext => {
182 let i = self.next_arg;
183 if self.check_positional_ok() {
188 parse::ArgumentIs(i) => Exact(i),
189 parse::ArgumentNamed(s) => Named(s.to_strbuf()),
192 // and finally the method being applied
195 let ty = Known(arg.format.ty.to_strbuf());
196 self.verify_arg_type(pos, ty);
198 Some(ref method) => { self.verify_method(pos, *method); }
204 fn verify_pieces(&mut self, pieces: &[parse::Piece]) {
205 for piece in pieces.iter() {
206 self.verify_piece(piece);
210 fn verify_count(&mut self, c: parse::Count) {
212 parse::CountImplied | parse::CountIs(..) => {}
213 parse::CountIsParam(i) => {
214 self.verify_arg_type(Exact(i), Unsigned);
216 parse::CountIsName(s) => {
217 self.verify_arg_type(Named(s.to_strbuf()), Unsigned);
219 parse::CountIsNextParam => {
220 if self.check_positional_ok() {
221 self.verify_arg_type(Exact(self.next_arg), Unsigned);
228 fn check_positional_ok(&mut self) -> bool {
229 if self.nest_level != 0 {
230 self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
231 arguments nested inside methods");
238 fn verify_method(&mut self, pos: Position, m: &parse::Method) {
239 self.nest_level += 1;
241 parse::Plural(_, ref arms, ref default) => {
242 let mut seen_cases = HashSet::new();
243 self.verify_arg_type(pos, Unsigned);
244 for arm in arms.iter() {
245 if !seen_cases.insert(arm.selector) {
247 parse::Keyword(name) => {
248 self.ecx.span_err(self.fmtsp,
249 format!("duplicate selector \
252 parse::Literal(idx) => {
253 self.ecx.span_err(self.fmtsp,
254 format!("duplicate selector \
259 self.verify_pieces(arm.result.as_slice());
261 self.verify_pieces(default.as_slice());
263 parse::Select(ref arms, ref default) => {
264 self.verify_arg_type(pos, String);
265 let mut seen_cases = HashSet::new();
266 for arm in arms.iter() {
267 if !seen_cases.insert(arm.selector) {
268 self.ecx.span_err(self.fmtsp,
269 format!("duplicate selector `{}`",
271 } else if arm.selector == "" {
272 self.ecx.span_err(self.fmtsp,
273 "empty selector in `select`");
275 self.verify_pieces(arm.result.as_slice());
277 self.verify_pieces(default.as_slice());
280 self.nest_level -= 1;
283 fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
286 if self.args.len() <= arg {
287 let msg = format!("invalid reference to argument `{}` (there \
288 are {} arguments)", arg, self.args.len());
289 self.ecx.span_err(self.fmtsp, msg);
293 let arg_type = match self.arg_types.get(arg) {
295 &Some(ref x) => Some(x)
297 self.verify_same(self.args.get(arg).span, &ty, arg_type);
299 if self.arg_types.get(arg).is_none() {
300 *self.arg_types.get_mut(arg) = Some(ty);
305 let span = match self.names.find(&name) {
308 let msg = format!("there is no argument named `{}`", name);
309 self.ecx.span_err(self.fmtsp, msg);
313 self.verify_same(span, &ty, self.name_types.find(&name));
314 if !self.name_types.contains_key(&name) {
315 self.name_types.insert(name.clone(), ty);
317 // Assign this named argument a slot in the arguments array if
318 // it hasn't already been assigned a slot.
319 if !self.name_positions.contains_key(&name) {
320 let slot = self.name_positions.len();
321 self.name_positions.insert(name, slot);
327 /// When we're keeping track of the types that are declared for certain
328 /// arguments, we assume that `None` means we haven't seen this argument
329 /// yet, `Some(None)` means that we've seen the argument, but no format was
330 /// specified, and `Some(Some(x))` means that the argument was declared to
333 /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
334 /// that: `Some(None) == Some(Some(x))`
335 fn verify_same(&self,
338 before: Option<&ArgumentType>) {
339 let cur = match before {
347 (&Known(ref cur), &Known(ref ty)) => {
348 self.ecx.span_err(sp,
349 format!("argument redeclared with type `{}` when \
350 it was previously `{}`",
354 (&Known(ref cur), _) => {
355 self.ecx.span_err(sp,
356 format!("argument used to format with `{}` was \
357 attempted to not be used for formatting",
360 (_, &Known(ref ty)) => {
361 self.ecx.span_err(sp,
362 format!("argument previously used as a format \
363 argument attempted to be used as `{}`",
367 self.ecx.span_err(sp, "argument declared with multiple formats");
372 /// These attributes are applied to all statics that this syntax extension
374 fn static_attrs(&self) -> Vec<ast::Attribute> {
375 // Flag statics as `address_insignificant` so LLVM can merge duplicate
376 // globals as much as possible (which we're generating a whole lot of).
377 let unnamed = self.ecx
378 .meta_word(self.fmtsp,
380 "address_insignificant"));
381 let unnamed = self.ecx.attribute(self.fmtsp, unnamed);
383 // Do not warn format string as dead code
384 let dead_code = self.ecx.meta_word(self.fmtsp,
385 InternedString::new("dead_code"));
386 let allow_dead_code = self.ecx.meta_list(self.fmtsp,
387 InternedString::new("allow"),
389 let allow_dead_code = self.ecx.attribute(self.fmtsp, allow_dead_code);
390 return vec!(unnamed, allow_dead_code);
393 fn rtpath(&self, s: &str) -> Vec<ast::Ident> {
394 vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
395 self.ecx.ident_of("rt"), self.ecx.ident_of(s))
398 fn none(&self) -> @ast::Expr {
399 let none = self.ecx.path_global(self.fmtsp, vec!(
400 self.ecx.ident_of("std"),
401 self.ecx.ident_of("option"),
402 self.ecx.ident_of("None")));
403 self.ecx.expr_path(none)
406 fn some(&self, e: @ast::Expr) -> @ast::Expr {
407 let p = self.ecx.path_global(self.fmtsp, vec!(
408 self.ecx.ident_of("std"),
409 self.ecx.ident_of("option"),
410 self.ecx.ident_of("Some")));
411 let p = self.ecx.expr_path(p);
412 self.ecx.expr_call(self.fmtsp, p, vec!(e))
415 fn trans_count(&self, c: parse::Count) -> @ast::Expr {
418 parse::CountIs(i) => {
419 self.ecx.expr_call_global(sp, self.rtpath("CountIs"),
420 vec!(self.ecx.expr_uint(sp, i)))
422 parse::CountIsParam(i) => {
423 self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"),
424 vec!(self.ecx.expr_uint(sp, i)))
426 parse::CountImplied => {
427 let path = self.ecx.path_global(sp, self.rtpath("CountImplied"));
428 self.ecx.expr_path(path)
430 parse::CountIsNextParam => {
431 let path = self.ecx.path_global(sp, self.rtpath("CountIsNextParam"));
432 self.ecx.expr_path(path)
434 parse::CountIsName(n) => {
435 let i = match self.name_positions.find_equiv(&n) {
437 None => 0, // error already emitted elsewhere
439 let i = i + self.args.len();
440 self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"),
441 vec!(self.ecx.expr_uint(sp, i)))
446 fn trans_method(&mut self, method: &parse::Method) -> @ast::Expr {
448 let method = match *method {
449 parse::Select(ref arms, ref default) => {
450 let arms = arms.iter().map(|arm| {
451 let p = self.ecx.path_global(sp, self.rtpath("SelectArm"));
452 let result = arm.result.iter().map(|p| {
455 let s = token::intern_and_get_ident(arm.selector);
456 let selector = self.ecx.expr_str(sp, s);
457 self.ecx.expr_struct(sp, p, vec!(
458 self.ecx.field_imm(sp,
459 self.ecx.ident_of("selector"),
461 self.ecx.field_imm(sp, self.ecx.ident_of("result"),
462 self.ecx.expr_vec_slice(sp, result))))
464 let default = default.iter().map(|p| {
467 self.ecx.expr_call_global(sp, self.rtpath("Select"), vec!(
468 self.ecx.expr_vec_slice(sp, arms),
469 self.ecx.expr_vec_slice(sp, default)))
471 parse::Plural(offset, ref arms, ref default) => {
472 let offset = match offset {
473 Some(i) => { self.some(self.ecx.expr_uint(sp, i)) }
474 None => { self.none() }
476 let arms = arms.iter().map(|arm| {
477 let p = self.ecx.path_global(sp, self.rtpath("PluralArm"));
478 let result = arm.result.iter().map(|p| {
481 let (lr, selarg) = match arm.selector {
482 parse::Keyword(t) => {
483 let p = self.rtpath(t.to_str());
484 let p = self.ecx.path_global(sp, p);
485 (self.rtpath("Keyword"), self.ecx.expr_path(p))
487 parse::Literal(i) => {
488 (self.rtpath("Literal"), self.ecx.expr_uint(sp, i))
491 let selector = self.ecx.expr_call_global(sp,
493 self.ecx.expr_struct(sp, p, vec!(
494 self.ecx.field_imm(sp,
495 self.ecx.ident_of("selector"),
497 self.ecx.field_imm(sp, self.ecx.ident_of("result"),
498 self.ecx.expr_vec_slice(sp, result))))
500 let default = default.iter().map(|p| {
503 self.ecx.expr_call_global(sp, self.rtpath("Plural"), vec!(
505 self.ecx.expr_vec_slice(sp, arms),
506 self.ecx.expr_vec_slice(sp, default)))
509 let life = self.ecx.lifetime(sp, self.ecx.ident_of("static").name);
510 let ty = self.ecx.ty_path(self.ecx.path_all(
513 self.rtpath("Method"),
517 let st = ast::ItemStatic(ty, ast::MutImmutable, method);
518 let static_name = self.ecx.ident_of(format!("__STATIC_METHOD_{}",
519 self.method_statics.len()));
520 let item = self.ecx.item(sp, static_name, self.static_attrs(), st);
521 self.method_statics.push(item);
522 self.ecx.expr_ident(sp, static_name)
525 /// Translate a `parse::Piece` to a static `rt::Piece`
526 fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr {
529 parse::String(s) => {
530 let s = token::intern_and_get_ident(s);
531 self.ecx.expr_call_global(sp,
532 self.rtpath("String"),
534 self.ecx.expr_str(sp, s)
537 parse::CurrentArgument => {
538 let nil = self.ecx.expr_lit(sp, ast::LitNil);
539 self.ecx.expr_call_global(sp, self.rtpath("CurrentArgument"), vec!(nil))
541 parse::Argument(ref arg) => {
542 // Translate the position
543 let pos = match arg.position {
544 // These two have a direct mapping
545 parse::ArgumentNext => {
546 let path = self.ecx.path_global(sp,
547 self.rtpath("ArgumentNext"));
548 self.ecx.expr_path(path)
550 parse::ArgumentIs(i) => {
551 self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"),
552 vec!(self.ecx.expr_uint(sp, i)))
554 // Named arguments are converted to positional arguments at
555 // the end of the list of arguments
556 parse::ArgumentNamed(n) => {
557 let i = match self.name_positions.find_equiv(&n) {
559 None => 0, // error already emitted elsewhere
561 let i = i + self.args.len();
562 self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"),
563 vec!(self.ecx.expr_uint(sp, i)))
567 // Translate the format
568 let fill = match arg.format.fill { Some(c) => c, None => ' ' };
569 let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
570 let align = match arg.format.align {
571 parse::AlignLeft => {
572 self.ecx.path_global(sp, self.rtpath("AlignLeft"))
574 parse::AlignRight => {
575 self.ecx.path_global(sp, self.rtpath("AlignRight"))
577 parse::AlignUnknown => {
578 self.ecx.path_global(sp, self.rtpath("AlignUnknown"))
581 let align = self.ecx.expr_path(align);
582 let flags = self.ecx.expr_uint(sp, arg.format.flags);
583 let prec = self.trans_count(arg.format.precision);
584 let width = self.trans_count(arg.format.width);
585 let path = self.ecx.path_global(sp, self.rtpath("FormatSpec"));
586 let fmt = self.ecx.expr_struct(sp, path, vec!(
587 self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
588 self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
589 self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
590 self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
591 self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
593 // Translate the method (if any)
594 let method = match arg.method {
595 None => { self.none() }
597 let m = self.trans_method(*m);
598 self.some(self.ecx.expr_addr_of(sp, m))
601 let path = self.ecx.path_global(sp, self.rtpath("Argument"));
602 let s = self.ecx.expr_struct(sp, path, vec!(
603 self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
604 self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt),
605 self.ecx.field_imm(sp, self.ecx.ident_of("method"), method)));
606 self.ecx.expr_call_global(sp, self.rtpath("Argument"), vec!(s))
611 /// Actually builds the expression which the iformat! block will be expanded
613 fn to_expr(&self, invocation: Invocation) -> @ast::Expr {
614 let mut lets = Vec::new();
615 let mut locals = Vec::new();
616 let mut names = Vec::from_fn(self.name_positions.len(), |_| None);
617 let mut pats = Vec::new();
618 let mut heads = Vec::new();
620 // First, declare all of our methods that are statics
621 for &method in self.method_statics.iter() {
622 let decl = respan(self.fmtsp, ast::DeclItem(method));
623 lets.push(@respan(self.fmtsp,
624 ast::StmtDecl(@decl, ast::DUMMY_NODE_ID)));
627 // Next, build up the static array which will become our precompiled
629 let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
630 let piece_ty = self.ecx.ty_path(self.ecx.path_all(
633 self.ecx.ident_of("std"),
634 self.ecx.ident_of("fmt"),
635 self.ecx.ident_of("rt"),
636 self.ecx.ident_of("Piece")),
637 vec!(self.ecx.lifetime(self.fmtsp,
638 self.ecx.ident_of("static").name)),
641 let ty = ast::TyFixedLengthVec(
643 self.ecx.expr_uint(self.fmtsp, self.pieces.len())
645 let ty = self.ecx.ty(self.fmtsp, ty);
646 let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
647 let static_name = self.ecx.ident_of("__STATIC_FMTSTR");
648 let item = self.ecx.item(self.fmtsp, static_name,
649 self.static_attrs(), st);
650 let decl = respan(self.fmtsp, ast::DeclItem(item));
651 lets.push(@respan(self.fmtsp, ast::StmtDecl(@decl, ast::DUMMY_NODE_ID)));
653 // Right now there is a bug such that for the expression:
655 // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
656 // vald for the call to `foo`. To work around this all arguments to the
657 // format! string are shoved into locals. Furthermore, we shove the address
658 // of each variable because we don't want to move out of the arguments
659 // passed to this function.
660 for (i, &e) in self.args.iter().enumerate() {
661 if self.arg_types.get(i).is_none() {
662 continue // error already generated
665 let name = self.ecx.ident_of(format!("__arg{}", i));
666 pats.push(self.ecx.pat_ident(e.span, name));
667 heads.push(self.ecx.expr_addr_of(e.span, e));
668 locals.push(self.format_arg(e.span, Exact(i),
669 self.ecx.expr_ident(e.span, name)));
671 for name in self.name_ordering.iter() {
672 let e = match self.names.find(name) {
673 Some(&e) if self.name_types.contains_key(name) => e,
674 Some(..) | None => continue
677 let lname = self.ecx.ident_of(format!("__arg{}", *name));
678 pats.push(self.ecx.pat_ident(e.span, lname));
679 heads.push(self.ecx.expr_addr_of(e.span, e));
680 *names.get_mut(*self.name_positions.get(name)) =
681 Some(self.format_arg(e.span,
682 Named((*name).clone()),
683 self.ecx.expr_ident(e.span, lname)));
686 // Now create a vector containing all the arguments
687 let slicename = self.ecx.ident_of("__args_vec");
689 let args = names.move_iter().map(|a| a.unwrap());
690 let mut args = locals.move_iter().chain(args);
691 let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
692 lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
695 // Now create the fmt::Arguments struct with all our locals we created.
696 let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
697 let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
698 let result = self.ecx.expr_call_global(self.fmtsp, vec!(
699 self.ecx.ident_of("std"),
700 self.ecx.ident_of("fmt"),
701 self.ecx.ident_of("Arguments"),
702 self.ecx.ident_of("new")), vec!(fmt, args_slice));
704 // We did all the work of making sure that the arguments
705 // structure is safe, so we can safely have an unsafe block.
706 let result = self.ecx.expr_block(P(ast::Block {
707 view_items: Vec::new(),
710 id: ast::DUMMY_NODE_ID,
711 rules: ast::UnsafeBlock(ast::CompilerGenerated),
714 let resname = self.ecx.ident_of("__args");
715 lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
716 let res = self.ecx.expr_ident(self.fmtsp, resname);
717 let result = match invocation {
719 self.ecx.expr_call(e.span, e,
720 vec!(self.ecx.expr_addr_of(e.span, res)))
722 MethodCall(e, m) => {
723 self.ecx.expr_method_call(e.span, e, m,
724 vec!(self.ecx.expr_addr_of(e.span, res)))
727 let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
730 // Constructs an AST equivalent to:
732 // match (&arg0, &arg1) {
733 // (tmp0, tmp1) => body
742 // Because of #11585 the new temporary lifetime rule, the enclosing
743 // statements for these temporaries become the let's themselves.
744 // If one or more of them are RefCell's, RefCell borrow() will also
745 // end there; they don't last long enough for body to use them. The
746 // match expression solves the scope problem.
748 // Note, it may also very well be transformed to:
753 // ref tmp1 => body } } }
755 // But the nested match expression is proved to perform not as well
756 // as series of let's; the first approach does.
757 let pat = self.ecx.pat(self.fmtsp, ast::PatTup(pats));
758 let arm = self.ecx.arm(self.fmtsp, vec!(pat), body);
759 let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
760 self.ecx.expr_match(self.fmtsp, head, vec!(arm))
763 fn format_arg(&self, sp: Span, argno: Position, arg: @ast::Expr)
765 let ty = match argno {
766 Exact(ref i) => self.arg_types.get(*i).get_ref(),
767 Named(ref s) => self.name_types.get(s)
770 let fmt_fn = match *ty {
771 Known(ref tyname) => {
772 match tyname.as_slice() {
774 "?" => "secret_poly",
775 "b" => "secret_bool",
776 "c" => "secret_char",
777 "d" | "i" => "secret_signed",
778 "e" => "secret_lower_exp",
779 "E" => "secret_upper_exp",
780 "f" => "secret_float",
781 "o" => "secret_octal",
782 "p" => "secret_pointer",
783 "s" => "secret_string",
784 "t" => "secret_binary",
785 "u" => "secret_unsigned",
786 "x" => "secret_lower_hex",
787 "X" => "secret_upper_hex",
789 self.ecx.span_err(sp, format!("unknown format trait `{}`",
796 return self.ecx.expr_call_global(sp, vec!(
797 self.ecx.ident_of("std"),
798 self.ecx.ident_of("fmt"),
799 self.ecx.ident_of("argumentstr")), vec!(arg))
802 return self.ecx.expr_call_global(sp, vec!(
803 self.ecx.ident_of("std"),
804 self.ecx.ident_of("fmt"),
805 self.ecx.ident_of("argumentuint")), vec!(arg))
809 let format_fn = self.ecx.path_global(sp, vec!(
810 self.ecx.ident_of("std"),
811 self.ecx.ident_of("fmt"),
812 self.ecx.ident_of(fmt_fn)));
813 self.ecx.expr_call_global(sp, vec!(
814 self.ecx.ident_of("std"),
815 self.ecx.ident_of("fmt"),
816 self.ecx.ident_of("argument")), vec!(self.ecx.expr_path(format_fn), arg))
820 pub fn expand_format_args(ecx: &mut ExtCtxt, sp: Span,
821 tts: &[ast::TokenTree]) -> Box<base::MacResult> {
823 match parse_args(ecx, sp, false, tts) {
824 (invocation, Some((efmt, args, order, names))) => {
825 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
828 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
832 pub fn expand_format_args_method(ecx: &mut ExtCtxt, sp: Span,
833 tts: &[ast::TokenTree]) -> Box<base::MacResult> {
835 match parse_args(ecx, sp, true, tts) {
836 (invocation, Some((efmt, args, order, names))) => {
837 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
840 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
844 /// Take the various parts of `format_args!(extra, efmt, args...,
845 /// name=names...)` and construct the appropriate formatting
847 pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
848 invocation: Invocation,
849 efmt: @ast::Expr, args: Vec<@ast::Expr>,
850 name_ordering: Vec<StrBuf>,
851 names: HashMap<StrBuf, @ast::Expr>) -> @ast::Expr {
852 let arg_types = Vec::from_fn(args.len(), |_| None);
853 let mut cx = Context {
856 arg_types: arg_types,
858 name_positions: HashMap::new(),
859 name_types: HashMap::new(),
860 name_ordering: name_ordering,
864 method_statics: Vec::new(),
867 cx.fmtsp = efmt.span;
868 let fmt = match expr_to_str(cx.ecx,
870 "format argument must be a string literal.") {
871 Some((fmt, _)) => fmt,
872 None => return DummyResult::raw_expr(sp)
875 let mut parser = parse::Parser::new(fmt.get());
877 match parser.next() {
879 if parser.errors.len() > 0 { break }
880 cx.verify_piece(&piece);
881 let piece = cx.trans_piece(&piece);
882 cx.pieces.push(piece);
887 match parser.errors.shift() {
889 cx.ecx.span_err(efmt.span,
890 format_strbuf!("invalid format string: {}",
892 return DummyResult::raw_expr(sp);
897 // Make sure that all arguments were used and all arguments have types.
898 for (i, ty) in cx.arg_types.iter().enumerate() {
900 cx.ecx.span_err(cx.args.get(i).span, "argument never used");
903 for (name, e) in cx.names.iter() {
904 if !cx.name_types.contains_key(name) {
905 cx.ecx.span_err(e.span, "named argument never used");
909 cx.to_expr(invocation)