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<String, @ast::Expr>,
49 name_types: HashMap<String, ArgumentType>,
50 name_ordering: Vec<String>,
52 // Collection of the compiled `rt::Piece` structures
53 pieces: Vec<@ast::Expr> ,
54 name_positions: HashMap<String, 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<String>,
78 HashMap<String, @ast::Expr>)>) {
79 let mut args = Vec::new();
80 let mut names = HashMap::<String, @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()).as_slice());
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) {
145 format!("duplicate argument named `{}`",
147 ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
151 order.push(name.to_string());
152 names.insert(name.to_string(), e);
154 args.push(p.parse_expr());
157 return (invocation, Some((fmtstr, args, order, names)));
160 impl<'a, 'b> Context<'a, 'b> {
161 /// Verifies one piece of a parse string. All errors are not emitted as
162 /// fatal so we can continue giving errors about this and possibly other
164 fn verify_piece(&mut self, p: &parse::Piece) {
166 parse::String(..) => {}
167 parse::CurrentArgument => {
168 if self.nest_level == 0 {
169 self.ecx.span_err(self.fmtsp,
170 "`#` reference used with nothing to \
174 parse::Argument(ref arg) => {
175 // width/precision first, if they have implicit positional
176 // parameters it makes more sense to consume them first.
177 self.verify_count(arg.format.width);
178 self.verify_count(arg.format.precision);
180 // argument second, if it's an implicit positional parameter
181 // it's written second, so it should come after width/precision.
182 let pos = match arg.position {
183 parse::ArgumentNext => {
184 let i = self.next_arg;
185 if self.check_positional_ok() {
190 parse::ArgumentIs(i) => Exact(i),
191 parse::ArgumentNamed(s) => Named(s.to_string()),
194 // and finally the method being applied
197 let ty = Known(arg.format.ty.to_string());
198 self.verify_arg_type(pos, ty);
200 Some(ref method) => { self.verify_method(pos, *method); }
206 fn verify_pieces(&mut self, pieces: &[parse::Piece]) {
207 for piece in pieces.iter() {
208 self.verify_piece(piece);
212 fn verify_count(&mut self, c: parse::Count) {
214 parse::CountImplied | parse::CountIs(..) => {}
215 parse::CountIsParam(i) => {
216 self.verify_arg_type(Exact(i), Unsigned);
218 parse::CountIsName(s) => {
219 self.verify_arg_type(Named(s.to_string()), Unsigned);
221 parse::CountIsNextParam => {
222 if self.check_positional_ok() {
223 self.verify_arg_type(Exact(self.next_arg), Unsigned);
230 fn check_positional_ok(&mut self) -> bool {
231 if self.nest_level != 0 {
232 self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
233 arguments nested inside methods");
240 fn verify_method(&mut self, pos: Position, m: &parse::Method) {
241 self.nest_level += 1;
243 parse::Plural(_, ref arms, ref default) => {
244 let mut seen_cases = HashSet::new();
245 self.verify_arg_type(pos, Unsigned);
246 for arm in arms.iter() {
247 if !seen_cases.insert(arm.selector) {
249 parse::Keyword(name) => {
250 self.ecx.span_err(self.fmtsp,
255 parse::Literal(idx) => {
256 self.ecx.span_err(self.fmtsp,
263 self.verify_pieces(arm.result.as_slice());
265 self.verify_pieces(default.as_slice());
267 parse::Select(ref arms, ref default) => {
268 self.verify_arg_type(pos, String);
269 let mut seen_cases = HashSet::new();
270 for arm in arms.iter() {
271 if !seen_cases.insert(arm.selector) {
272 self.ecx.span_err(self.fmtsp,
273 format!("duplicate selector `{}`",
274 arm.selector).as_slice());
275 } else if arm.selector == "" {
276 self.ecx.span_err(self.fmtsp,
277 "empty selector in `select`");
279 self.verify_pieces(arm.result.as_slice());
281 self.verify_pieces(default.as_slice());
284 self.nest_level -= 1;
287 fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
290 if self.args.len() <= arg {
291 let msg = format!("invalid reference to argument `{}` (there \
292 are {} arguments)", arg, self.args.len());
293 self.ecx.span_err(self.fmtsp, msg.as_slice());
297 let arg_type = match self.arg_types.get(arg) {
299 &Some(ref x) => Some(x)
301 self.verify_same(self.args.get(arg).span, &ty, arg_type);
303 if self.arg_types.get(arg).is_none() {
304 *self.arg_types.get_mut(arg) = Some(ty);
309 let span = match self.names.find(&name) {
312 let msg = format!("there is no argument named `{}`", name);
313 self.ecx.span_err(self.fmtsp, msg.as_slice());
317 self.verify_same(span, &ty, self.name_types.find(&name));
318 if !self.name_types.contains_key(&name) {
319 self.name_types.insert(name.clone(), ty);
321 // Assign this named argument a slot in the arguments array if
322 // it hasn't already been assigned a slot.
323 if !self.name_positions.contains_key(&name) {
324 let slot = self.name_positions.len();
325 self.name_positions.insert(name, slot);
331 /// When we're keeping track of the types that are declared for certain
332 /// arguments, we assume that `None` means we haven't seen this argument
333 /// yet, `Some(None)` means that we've seen the argument, but no format was
334 /// specified, and `Some(Some(x))` means that the argument was declared to
337 /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
338 /// that: `Some(None) == Some(Some(x))`
339 fn verify_same(&self,
342 before: Option<&ArgumentType>) {
343 let cur = match before {
351 (&Known(ref cur), &Known(ref ty)) => {
352 self.ecx.span_err(sp,
353 format!("argument redeclared with type `{}` when \
354 it was previously `{}`",
358 (&Known(ref cur), _) => {
359 self.ecx.span_err(sp,
360 format!("argument used to format with `{}` was \
361 attempted to not be used for formatting",
364 (_, &Known(ref ty)) => {
365 self.ecx.span_err(sp,
366 format!("argument previously used as a format \
367 argument attempted to be used as `{}`",
371 self.ecx.span_err(sp, "argument declared with multiple formats");
376 /// These attributes are applied to all statics that this syntax extension
378 fn static_attrs(&self) -> Vec<ast::Attribute> {
379 // Flag statics as `address_insignificant` so LLVM can merge duplicate
380 // globals as much as possible (which we're generating a whole lot of).
381 let unnamed = self.ecx
382 .meta_word(self.fmtsp,
384 "address_insignificant"));
385 let unnamed = self.ecx.attribute(self.fmtsp, unnamed);
387 // Do not warn format string as dead code
388 let dead_code = self.ecx.meta_word(self.fmtsp,
389 InternedString::new("dead_code"));
390 let allow_dead_code = self.ecx.meta_list(self.fmtsp,
391 InternedString::new("allow"),
393 let allow_dead_code = self.ecx.attribute(self.fmtsp, allow_dead_code);
394 return vec!(unnamed, allow_dead_code);
397 fn rtpath(&self, s: &str) -> Vec<ast::Ident> {
398 vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
399 self.ecx.ident_of("rt"), self.ecx.ident_of(s))
402 fn none(&self) -> @ast::Expr {
403 let none = self.ecx.path_global(self.fmtsp, vec!(
404 self.ecx.ident_of("std"),
405 self.ecx.ident_of("option"),
406 self.ecx.ident_of("None")));
407 self.ecx.expr_path(none)
410 fn some(&self, e: @ast::Expr) -> @ast::Expr {
411 let p = self.ecx.path_global(self.fmtsp, vec!(
412 self.ecx.ident_of("std"),
413 self.ecx.ident_of("option"),
414 self.ecx.ident_of("Some")));
415 let p = self.ecx.expr_path(p);
416 self.ecx.expr_call(self.fmtsp, p, vec!(e))
419 fn trans_count(&self, c: parse::Count) -> @ast::Expr {
422 parse::CountIs(i) => {
423 self.ecx.expr_call_global(sp, self.rtpath("CountIs"),
424 vec!(self.ecx.expr_uint(sp, i)))
426 parse::CountIsParam(i) => {
427 self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"),
428 vec!(self.ecx.expr_uint(sp, i)))
430 parse::CountImplied => {
431 let path = self.ecx.path_global(sp, self.rtpath("CountImplied"));
432 self.ecx.expr_path(path)
434 parse::CountIsNextParam => {
435 let path = self.ecx.path_global(sp, self.rtpath("CountIsNextParam"));
436 self.ecx.expr_path(path)
438 parse::CountIsName(n) => {
439 let i = match self.name_positions.find_equiv(&n) {
441 None => 0, // error already emitted elsewhere
443 let i = i + self.args.len();
444 self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"),
445 vec!(self.ecx.expr_uint(sp, i)))
450 fn trans_method(&mut self, method: &parse::Method) -> @ast::Expr {
452 let method = match *method {
453 parse::Select(ref arms, ref default) => {
454 let arms = arms.iter().map(|arm| {
455 let p = self.ecx.path_global(sp, self.rtpath("SelectArm"));
456 let result = arm.result.iter().map(|p| {
459 let s = token::intern_and_get_ident(arm.selector);
460 let selector = self.ecx.expr_str(sp, s);
461 self.ecx.expr_struct(sp, p, vec!(
462 self.ecx.field_imm(sp,
463 self.ecx.ident_of("selector"),
465 self.ecx.field_imm(sp, self.ecx.ident_of("result"),
466 self.ecx.expr_vec_slice(sp, result))))
468 let default = default.iter().map(|p| {
471 self.ecx.expr_call_global(sp, self.rtpath("Select"), vec!(
472 self.ecx.expr_vec_slice(sp, arms),
473 self.ecx.expr_vec_slice(sp, default)))
475 parse::Plural(offset, ref arms, ref default) => {
476 let offset = match offset {
477 Some(i) => { self.some(self.ecx.expr_uint(sp, i)) }
478 None => { self.none() }
480 let arms = arms.iter().map(|arm| {
481 let p = self.ecx.path_global(sp, self.rtpath("PluralArm"));
482 let result = arm.result.iter().map(|p| {
485 let (lr, selarg) = match arm.selector {
486 parse::Keyword(t) => {
487 let p = self.rtpath(t.to_str().as_slice());
488 let p = self.ecx.path_global(sp, p);
489 (self.rtpath("Keyword"), self.ecx.expr_path(p))
491 parse::Literal(i) => {
492 (self.rtpath("Literal"), self.ecx.expr_uint(sp, i))
495 let selector = self.ecx.expr_call_global(sp,
497 self.ecx.expr_struct(sp, p, vec!(
498 self.ecx.field_imm(sp,
499 self.ecx.ident_of("selector"),
501 self.ecx.field_imm(sp, self.ecx.ident_of("result"),
502 self.ecx.expr_vec_slice(sp, result))))
504 let default = default.iter().map(|p| {
507 self.ecx.expr_call_global(sp, self.rtpath("Plural"), vec!(
509 self.ecx.expr_vec_slice(sp, arms),
510 self.ecx.expr_vec_slice(sp, default)))
513 let life = self.ecx.lifetime(sp, self.ecx.ident_of("static").name);
514 let ty = self.ecx.ty_path(self.ecx.path_all(
517 self.rtpath("Method"),
521 let st = ast::ItemStatic(ty, ast::MutImmutable, method);
522 let static_name = self.ecx.ident_of(format!("__STATIC_METHOD_{}",
525 let item = self.ecx.item(sp, static_name, self.static_attrs(), st);
526 self.method_statics.push(item);
527 self.ecx.expr_ident(sp, static_name)
530 /// Translate a `parse::Piece` to a static `rt::Piece`
531 fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr {
534 parse::String(s) => {
535 let s = token::intern_and_get_ident(s);
536 self.ecx.expr_call_global(sp,
537 self.rtpath("String"),
539 self.ecx.expr_str(sp, s)
542 parse::CurrentArgument => {
543 let nil = self.ecx.expr_lit(sp, ast::LitNil);
544 self.ecx.expr_call_global(sp, self.rtpath("CurrentArgument"), vec!(nil))
546 parse::Argument(ref arg) => {
547 // Translate the position
548 let pos = match arg.position {
549 // These two have a direct mapping
550 parse::ArgumentNext => {
551 let path = self.ecx.path_global(sp,
552 self.rtpath("ArgumentNext"));
553 self.ecx.expr_path(path)
555 parse::ArgumentIs(i) => {
556 self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"),
557 vec!(self.ecx.expr_uint(sp, i)))
559 // Named arguments are converted to positional arguments at
560 // the end of the list of arguments
561 parse::ArgumentNamed(n) => {
562 let i = match self.name_positions.find_equiv(&n) {
564 None => 0, // error already emitted elsewhere
566 let i = i + self.args.len();
567 self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"),
568 vec!(self.ecx.expr_uint(sp, i)))
572 // Translate the format
573 let fill = match arg.format.fill { Some(c) => c, None => ' ' };
574 let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
575 let align = match arg.format.align {
576 parse::AlignLeft => {
577 self.ecx.path_global(sp, self.rtpath("AlignLeft"))
579 parse::AlignRight => {
580 self.ecx.path_global(sp, self.rtpath("AlignRight"))
582 parse::AlignUnknown => {
583 self.ecx.path_global(sp, self.rtpath("AlignUnknown"))
586 let align = self.ecx.expr_path(align);
587 let flags = self.ecx.expr_uint(sp, arg.format.flags);
588 let prec = self.trans_count(arg.format.precision);
589 let width = self.trans_count(arg.format.width);
590 let path = self.ecx.path_global(sp, self.rtpath("FormatSpec"));
591 let fmt = self.ecx.expr_struct(sp, path, vec!(
592 self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
593 self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
594 self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
595 self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
596 self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
598 // Translate the method (if any)
599 let method = match arg.method {
600 None => { self.none() }
602 let m = self.trans_method(*m);
603 self.some(self.ecx.expr_addr_of(sp, m))
606 let path = self.ecx.path_global(sp, self.rtpath("Argument"));
607 let s = self.ecx.expr_struct(sp, path, vec!(
608 self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
609 self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt),
610 self.ecx.field_imm(sp, self.ecx.ident_of("method"), method)));
611 self.ecx.expr_call_global(sp, self.rtpath("Argument"), vec!(s))
616 /// Actually builds the expression which the iformat! block will be expanded
618 fn to_expr(&self, invocation: Invocation) -> @ast::Expr {
619 let mut lets = Vec::new();
620 let mut locals = Vec::new();
621 let mut names = Vec::from_fn(self.name_positions.len(), |_| None);
622 let mut pats = Vec::new();
623 let mut heads = Vec::new();
625 // First, declare all of our methods that are statics
626 for &method in self.method_statics.iter() {
627 let decl = respan(self.fmtsp, ast::DeclItem(method));
628 lets.push(@respan(self.fmtsp,
629 ast::StmtDecl(@decl, ast::DUMMY_NODE_ID)));
632 // Next, build up the static array which will become our precompiled
634 let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
635 let piece_ty = self.ecx.ty_path(self.ecx.path_all(
638 self.ecx.ident_of("std"),
639 self.ecx.ident_of("fmt"),
640 self.ecx.ident_of("rt"),
641 self.ecx.ident_of("Piece")),
642 vec!(self.ecx.lifetime(self.fmtsp,
643 self.ecx.ident_of("static").name)),
646 let ty = ast::TyFixedLengthVec(
648 self.ecx.expr_uint(self.fmtsp, self.pieces.len())
650 let ty = self.ecx.ty(self.fmtsp, ty);
651 let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
652 let static_name = self.ecx.ident_of("__STATIC_FMTSTR");
653 let item = self.ecx.item(self.fmtsp, static_name,
654 self.static_attrs(), st);
655 let decl = respan(self.fmtsp, ast::DeclItem(item));
656 lets.push(@respan(self.fmtsp, ast::StmtDecl(@decl, ast::DUMMY_NODE_ID)));
658 // Right now there is a bug such that for the expression:
660 // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
661 // vald for the call to `foo`. To work around this all arguments to the
662 // format! string are shoved into locals. Furthermore, we shove the address
663 // of each variable because we don't want to move out of the arguments
664 // passed to this function.
665 for (i, &e) in self.args.iter().enumerate() {
666 if self.arg_types.get(i).is_none() {
667 continue // error already generated
670 let name = self.ecx.ident_of(format!("__arg{}", i).as_slice());
671 pats.push(self.ecx.pat_ident(e.span, name));
672 heads.push(self.ecx.expr_addr_of(e.span, e));
673 locals.push(self.format_arg(e.span, Exact(i),
674 self.ecx.expr_ident(e.span, name)));
676 for name in self.name_ordering.iter() {
677 let e = match self.names.find(name) {
678 Some(&e) if self.name_types.contains_key(name) => e,
679 Some(..) | None => continue
682 let lname = self.ecx.ident_of(format!("__arg{}",
684 pats.push(self.ecx.pat_ident(e.span, lname));
685 heads.push(self.ecx.expr_addr_of(e.span, e));
686 *names.get_mut(*self.name_positions.get(name)) =
687 Some(self.format_arg(e.span,
688 Named((*name).clone()),
689 self.ecx.expr_ident(e.span, lname)));
692 // Now create a vector containing all the arguments
693 let slicename = self.ecx.ident_of("__args_vec");
695 let args = names.move_iter().map(|a| a.unwrap());
696 let mut args = locals.move_iter().chain(args);
697 let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
698 lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
701 // Now create the fmt::Arguments struct with all our locals we created.
702 let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
703 let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
704 let result = self.ecx.expr_call_global(self.fmtsp, vec!(
705 self.ecx.ident_of("std"),
706 self.ecx.ident_of("fmt"),
707 self.ecx.ident_of("Arguments"),
708 self.ecx.ident_of("new")), vec!(fmt, args_slice));
710 // We did all the work of making sure that the arguments
711 // structure is safe, so we can safely have an unsafe block.
712 let result = self.ecx.expr_block(P(ast::Block {
713 view_items: Vec::new(),
716 id: ast::DUMMY_NODE_ID,
717 rules: ast::UnsafeBlock(ast::CompilerGenerated),
720 let resname = self.ecx.ident_of("__args");
721 lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
722 let res = self.ecx.expr_ident(self.fmtsp, resname);
723 let result = match invocation {
725 self.ecx.expr_call(e.span, e,
726 vec!(self.ecx.expr_addr_of(e.span, res)))
728 MethodCall(e, m) => {
729 self.ecx.expr_method_call(e.span, e, m,
730 vec!(self.ecx.expr_addr_of(e.span, res)))
733 let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
736 // Constructs an AST equivalent to:
738 // match (&arg0, &arg1) {
739 // (tmp0, tmp1) => body
748 // Because of #11585 the new temporary lifetime rule, the enclosing
749 // statements for these temporaries become the let's themselves.
750 // If one or more of them are RefCell's, RefCell borrow() will also
751 // end there; they don't last long enough for body to use them. The
752 // match expression solves the scope problem.
754 // Note, it may also very well be transformed to:
759 // ref tmp1 => body } } }
761 // But the nested match expression is proved to perform not as well
762 // as series of let's; the first approach does.
763 let pat = self.ecx.pat(self.fmtsp, ast::PatTup(pats));
764 let arm = self.ecx.arm(self.fmtsp, vec!(pat), body);
765 let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
766 self.ecx.expr_match(self.fmtsp, head, vec!(arm))
769 fn format_arg(&self, sp: Span, argno: Position, arg: @ast::Expr)
771 let ty = match argno {
772 Exact(ref i) => self.arg_types.get(*i).get_ref(),
773 Named(ref s) => self.name_types.get(s)
776 let (krate, fmt_fn) = match *ty {
777 Known(ref tyname) => {
778 match tyname.as_slice() {
779 "" => ("std", "secret_show"),
780 "?" => ("debug", "secret_poly"),
781 "b" => ("std", "secret_bool"),
782 "c" => ("std", "secret_char"),
783 "d" | "i" => ("std", "secret_signed"),
784 "e" => ("std", "secret_lower_exp"),
785 "E" => ("std", "secret_upper_exp"),
786 "f" => ("std", "secret_float"),
787 "o" => ("std", "secret_octal"),
788 "p" => ("std", "secret_pointer"),
789 "s" => ("std", "secret_string"),
790 "t" => ("std", "secret_binary"),
791 "u" => ("std", "secret_unsigned"),
792 "x" => ("std", "secret_lower_hex"),
793 "X" => ("std", "secret_upper_hex"),
797 format!("unknown format trait `{}`",
798 *tyname).as_slice());
804 return self.ecx.expr_call_global(sp, vec!(
805 self.ecx.ident_of("std"),
806 self.ecx.ident_of("fmt"),
807 self.ecx.ident_of("argumentstr")), vec!(arg))
810 return self.ecx.expr_call_global(sp, vec!(
811 self.ecx.ident_of("std"),
812 self.ecx.ident_of("fmt"),
813 self.ecx.ident_of("argumentuint")), vec!(arg))
817 let format_fn = self.ecx.path_global(sp, vec!(
818 self.ecx.ident_of(krate),
819 self.ecx.ident_of("fmt"),
820 self.ecx.ident_of(fmt_fn)));
821 self.ecx.expr_call_global(sp, vec!(
822 self.ecx.ident_of("std"),
823 self.ecx.ident_of("fmt"),
824 self.ecx.ident_of("argument")), vec!(self.ecx.expr_path(format_fn), arg))
828 pub fn expand_format_args(ecx: &mut ExtCtxt, sp: Span,
829 tts: &[ast::TokenTree]) -> Box<base::MacResult> {
831 match parse_args(ecx, sp, false, tts) {
832 (invocation, Some((efmt, args, order, names))) => {
833 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
836 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
840 pub fn expand_format_args_method(ecx: &mut ExtCtxt, sp: Span,
841 tts: &[ast::TokenTree]) -> Box<base::MacResult> {
843 match parse_args(ecx, sp, true, tts) {
844 (invocation, Some((efmt, args, order, names))) => {
845 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
848 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
852 /// Take the various parts of `format_args!(extra, efmt, args...,
853 /// name=names...)` and construct the appropriate formatting
855 pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
856 invocation: Invocation,
857 efmt: @ast::Expr, args: Vec<@ast::Expr>,
858 name_ordering: Vec<String>,
859 names: HashMap<String, @ast::Expr>) -> @ast::Expr {
860 let arg_types = Vec::from_fn(args.len(), |_| None);
861 let mut cx = Context {
864 arg_types: arg_types,
866 name_positions: HashMap::new(),
867 name_types: HashMap::new(),
868 name_ordering: name_ordering,
872 method_statics: Vec::new(),
875 cx.fmtsp = efmt.span;
876 let fmt = match expr_to_str(cx.ecx,
878 "format argument must be a string literal.") {
879 Some((fmt, _)) => fmt,
880 None => return DummyResult::raw_expr(sp)
883 let mut parser = parse::Parser::new(fmt.get());
885 match parser.next() {
887 if parser.errors.len() > 0 { break }
888 cx.verify_piece(&piece);
889 let piece = cx.trans_piece(&piece);
890 cx.pieces.push(piece);
895 match parser.errors.shift() {
897 cx.ecx.span_err(efmt.span,
898 format_strbuf!("invalid format string: {}",
900 return DummyResult::raw_expr(sp);
905 // Make sure that all arguments were used and all arguments have types.
906 for (i, ty) in cx.arg_types.iter().enumerate() {
908 cx.ecx.span_err(cx.args.get(i).span, "argument never used");
911 for (name, e) in cx.names.iter() {
912 if !cx.name_types.contains_key(name) {
913 cx.ecx.span_err(e.span, "named argument never used");
917 cx.to_expr(invocation)