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;
18 use parse::token::InternedString;
23 use collections::{HashMap, HashSet};
39 ecx: &'a mut ExtCtxt<'a>,
42 // Parsed argument expressions and the types that we've found so far for
44 args: Vec<@ast::Expr>,
45 arg_types: Vec<Option<ArgumentType>>,
46 // Parsed named expressions and the types that we've found for them so far.
47 // Note that we keep a side-array of the ordering of the named arguments
48 // found to be sure that we can translate them in the same order that they
50 names: HashMap<~str, @ast::Expr>,
51 name_types: HashMap<~str, ArgumentType>,
52 name_ordering: ~[~str],
54 // Collection of the compiled `rt::Piece` structures
55 pieces: Vec<@ast::Expr> ,
56 name_positions: HashMap<~str, uint>,
57 method_statics: Vec<@ast::Item> ,
59 // Updated as arguments are consumed or methods are entered
64 /// Parses the arguments from the given list of tokens, returning None
65 /// if there's a parse error so we can continue parsing other format!
68 /// If parsing succeeds, the second return value is:
70 /// Some((fmtstr, unnamed arguments, ordering of named arguments,
72 fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
73 -> (@ast::Expr, Option<(@ast::Expr, Vec<@ast::Expr>, ~[~str],
74 HashMap<~str, @ast::Expr>)>) {
75 let mut args = Vec::new();
76 let mut names = HashMap::<~str, @ast::Expr>::new();
79 let mut p = rsparse::new_parser_from_tts(ecx.parse_sess(),
82 // Parse the leading function expression (maybe a block, maybe a path)
83 let extra = p.parse_expr();
84 if !p.eat(&token::COMMA) {
85 ecx.span_err(sp, "expected token: `,`");
89 if p.token == token::EOF {
90 ecx.span_err(sp, "requires at least a format string argument");
93 let fmtstr = p.parse_expr();
94 let mut named = false;
95 while p.token != token::EOF {
96 if !p.eat(&token::COMMA) {
97 ecx.span_err(sp, "expected token: `,`");
100 if p.token == token::EOF { break } // accept trailing commas
101 if named || (token::is_ident(&p.token) &&
102 p.look_ahead(1, |t| *t == token::EQ)) {
104 let ident = match p.token {
105 token::IDENT(i, _) => {
111 "expected ident, positional arguments \
112 cannot follow named arguments");
113 return (extra, None);
117 format!("expected ident for named argument, but found `{}`",
118 p.this_token_to_str()));
119 return (extra, None);
122 let interned_name = token::get_ident(ident);
123 let name = interned_name.get();
124 p.expect(&token::EQ);
125 let e = p.parse_expr();
126 match names.find_equiv(&name) {
129 ecx.span_err(e.span, format!("duplicate argument named `{}`", name));
130 ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
134 order.push(name.to_str());
135 names.insert(name.to_str(), e);
137 args.push(p.parse_expr());
140 return (extra, Some((fmtstr, args, order, names)));
143 impl<'a> Context<'a> {
144 /// Verifies one piece of a parse string. All errors are not emitted as
145 /// fatal so we can continue giving errors about this and possibly other
147 fn verify_piece(&mut self, p: &parse::Piece) {
149 parse::String(..) => {}
150 parse::CurrentArgument => {
151 if self.nest_level == 0 {
152 self.ecx.span_err(self.fmtsp,
153 "`#` reference used with nothing to \
157 parse::Argument(ref arg) => {
158 // width/precision first, if they have implicit positional
159 // parameters it makes more sense to consume them first.
160 self.verify_count(arg.format.width);
161 self.verify_count(arg.format.precision);
163 // argument second, if it's an implicit positional parameter
164 // it's written second, so it should come after width/precision.
165 let pos = match arg.position {
166 parse::ArgumentNext => {
167 let i = self.next_arg;
168 if self.check_positional_ok() {
173 parse::ArgumentIs(i) => Exact(i),
174 parse::ArgumentNamed(s) => Named(s.to_str()),
177 // and finally the method being applied
180 let ty = Known(arg.format.ty.to_str());
181 self.verify_arg_type(pos, ty);
183 Some(ref method) => { self.verify_method(pos, *method); }
189 fn verify_pieces(&mut self, pieces: &[parse::Piece]) {
190 for piece in pieces.iter() {
191 self.verify_piece(piece);
195 fn verify_count(&mut self, c: parse::Count) {
197 parse::CountImplied | parse::CountIs(..) => {}
198 parse::CountIsParam(i) => {
199 self.verify_arg_type(Exact(i), Unsigned);
201 parse::CountIsName(s) => {
202 self.verify_arg_type(Named(s.to_str()), Unsigned);
204 parse::CountIsNextParam => {
205 if self.check_positional_ok() {
206 self.verify_arg_type(Exact(self.next_arg), Unsigned);
213 fn check_positional_ok(&mut self) -> bool {
214 if self.nest_level != 0 {
215 self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
216 arguments nested inside methods");
223 fn verify_method(&mut self, pos: Position, m: &parse::Method) {
224 self.nest_level += 1;
226 parse::Plural(_, ref arms, ref default) => {
227 let mut seen_cases = HashSet::new();
228 self.verify_arg_type(pos, Unsigned);
229 for arm in arms.iter() {
230 if !seen_cases.insert(arm.selector) {
232 parse::Keyword(name) => {
233 self.ecx.span_err(self.fmtsp,
234 format!("duplicate selector \
237 parse::Literal(idx) => {
238 self.ecx.span_err(self.fmtsp,
239 format!("duplicate selector \
244 self.verify_pieces(arm.result);
246 self.verify_pieces(*default);
248 parse::Select(ref arms, ref default) => {
249 self.verify_arg_type(pos, String);
250 let mut seen_cases = HashSet::new();
251 for arm in arms.iter() {
252 if !seen_cases.insert(arm.selector) {
253 self.ecx.span_err(self.fmtsp,
254 format!("duplicate selector `{}`",
256 } else if arm.selector == "" {
257 self.ecx.span_err(self.fmtsp,
258 "empty selector in `select`");
260 self.verify_pieces(arm.result);
262 self.verify_pieces(*default);
265 self.nest_level -= 1;
268 fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
271 if arg < 0 || self.args.len() <= arg {
272 let msg = format!("invalid reference to argument `{}` (there \
273 are {} arguments)", arg, self.args.len());
274 self.ecx.span_err(self.fmtsp, msg);
278 let arg_type = match self.arg_types[arg] {
280 Some(ref x) => Some(x)
282 self.verify_same(self.args[arg].span, &ty, arg_type);
284 if self.arg_types[arg].is_none() {
285 self.arg_types[arg] = Some(ty);
290 let span = match self.names.find(&name) {
293 let msg = format!("there is no argument named `{}`", name);
294 self.ecx.span_err(self.fmtsp, msg);
298 self.verify_same(span, &ty, self.name_types.find(&name));
299 if !self.name_types.contains_key(&name) {
300 self.name_types.insert(name.clone(), ty);
302 // Assign this named argument a slot in the arguments array if
303 // it hasn't already been assigned a slot.
304 if !self.name_positions.contains_key(&name) {
305 let slot = self.name_positions.len();
306 self.name_positions.insert(name, slot);
312 /// When we're keeping track of the types that are declared for certain
313 /// arguments, we assume that `None` means we haven't seen this argument
314 /// yet, `Some(None)` means that we've seen the argument, but no format was
315 /// specified, and `Some(Some(x))` means that the argument was declared to
318 /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
319 /// that: `Some(None) == Some(Some(x))`
320 fn verify_same(&self,
323 before: Option<&ArgumentType>) {
324 let cur = match before {
332 (&Known(ref cur), &Known(ref ty)) => {
333 self.ecx.span_err(sp,
334 format!("argument redeclared with type `{}` when \
335 it was previously `{}`",
339 (&Known(ref cur), _) => {
340 self.ecx.span_err(sp,
341 format!("argument used to format with `{}` was \
342 attempted to not be used for formatting",
345 (_, &Known(ref ty)) => {
346 self.ecx.span_err(sp,
347 format!("argument previously used as a format \
348 argument attempted to be used as `{}`",
352 self.ecx.span_err(sp, "argument declared with multiple formats");
357 /// These attributes are applied to all statics that this syntax extension
359 fn static_attrs(&self) -> Vec<ast::Attribute> {
360 // Flag statics as `address_insignificant` so LLVM can merge duplicate
361 // globals as much as possible (which we're generating a whole lot of).
362 let unnamed = self.ecx
363 .meta_word(self.fmtsp,
365 "address_insignificant"));
366 let unnamed = self.ecx.attribute(self.fmtsp, unnamed);
368 // Do not warn format string as dead code
369 let dead_code = self.ecx.meta_word(self.fmtsp,
370 InternedString::new("dead_code"));
371 let allow_dead_code = self.ecx.meta_list(self.fmtsp,
372 InternedString::new("allow"),
374 let allow_dead_code = self.ecx.attribute(self.fmtsp, allow_dead_code);
375 return vec!(unnamed, allow_dead_code);
378 fn parsepath(&self, s: &str) -> Vec<ast::Ident> {
379 vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
380 self.ecx.ident_of("parse"), self.ecx.ident_of(s))
383 fn rtpath(&self, s: &str) -> Vec<ast::Ident> {
384 vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
385 self.ecx.ident_of("rt"), self.ecx.ident_of(s))
388 fn ctpath(&self, s: &str) -> Vec<ast::Ident> {
389 vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
390 self.ecx.ident_of("parse"), self.ecx.ident_of(s))
393 fn none(&self) -> @ast::Expr {
394 let none = self.ecx.path_global(self.fmtsp, vec!(
395 self.ecx.ident_of("std"),
396 self.ecx.ident_of("option"),
397 self.ecx.ident_of("None")));
398 self.ecx.expr_path(none)
401 fn some(&self, e: @ast::Expr) -> @ast::Expr {
402 let p = self.ecx.path_global(self.fmtsp, vec!(
403 self.ecx.ident_of("std"),
404 self.ecx.ident_of("option"),
405 self.ecx.ident_of("Some")));
406 let p = self.ecx.expr_path(p);
407 self.ecx.expr_call(self.fmtsp, p, vec!(e))
410 fn trans_count(&self, c: parse::Count) -> @ast::Expr {
413 parse::CountIs(i) => {
414 self.ecx.expr_call_global(sp, self.rtpath("CountIs"),
415 vec!(self.ecx.expr_uint(sp, i)))
417 parse::CountIsParam(i) => {
418 self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"),
419 vec!(self.ecx.expr_uint(sp, i)))
421 parse::CountImplied => {
422 let path = self.ecx.path_global(sp, self.rtpath("CountImplied"));
423 self.ecx.expr_path(path)
425 parse::CountIsNextParam => {
426 let path = self.ecx.path_global(sp, self.rtpath("CountIsNextParam"));
427 self.ecx.expr_path(path)
429 parse::CountIsName(n) => {
430 let i = match self.name_positions.find_equiv(&n) {
432 None => 0, // error already emitted elsewhere
434 let i = i + self.args.len();
435 self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"),
436 vec!(self.ecx.expr_uint(sp, i)))
441 fn trans_method(&mut self, method: &parse::Method) -> @ast::Expr {
443 let method = match *method {
444 parse::Select(ref arms, ref default) => {
445 let arms = arms.iter().map(|arm| {
446 let p = self.ecx.path_global(sp, self.rtpath("SelectArm"));
447 let result = arm.result.iter().map(|p| {
450 let s = token::intern_and_get_ident(arm.selector);
451 let selector = self.ecx.expr_str(sp, s);
452 self.ecx.expr_struct(sp, p, vec!(
453 self.ecx.field_imm(sp,
454 self.ecx.ident_of("selector"),
456 self.ecx.field_imm(sp, self.ecx.ident_of("result"),
457 self.ecx.expr_vec_slice(sp, result))))
459 let default = default.iter().map(|p| {
462 self.ecx.expr_call_global(sp, self.rtpath("Select"), vec!(
463 self.ecx.expr_vec_slice(sp, arms),
464 self.ecx.expr_vec_slice(sp, default)))
466 parse::Plural(offset, ref arms, ref default) => {
467 let offset = match offset {
468 Some(i) => { self.some(self.ecx.expr_uint(sp, i)) }
469 None => { self.none() }
471 let arms = arms.iter().map(|arm| {
472 let p = self.ecx.path_global(sp, self.rtpath("PluralArm"));
473 let result = arm.result.iter().map(|p| {
476 let (lr, selarg) = match arm.selector {
477 parse::Keyword(t) => {
478 let p = self.ctpath(format!("{:?}", t));
479 let p = self.ecx.path_global(sp, p);
480 (self.rtpath("Keyword"), self.ecx.expr_path(p))
482 parse::Literal(i) => {
483 (self.rtpath("Literal"), self.ecx.expr_uint(sp, i))
486 let selector = self.ecx.expr_call_global(sp,
488 self.ecx.expr_struct(sp, p, vec!(
489 self.ecx.field_imm(sp,
490 self.ecx.ident_of("selector"),
492 self.ecx.field_imm(sp, self.ecx.ident_of("result"),
493 self.ecx.expr_vec_slice(sp, result))))
495 let default = default.iter().map(|p| {
498 self.ecx.expr_call_global(sp, self.rtpath("Plural"), vec!(
500 self.ecx.expr_vec_slice(sp, arms),
501 self.ecx.expr_vec_slice(sp, default)))
504 let life = self.ecx.lifetime(sp, self.ecx.ident_of("static").name);
505 let ty = self.ecx.ty_path(self.ecx.path_all(
508 self.rtpath("Method"),
512 let st = ast::ItemStatic(ty, ast::MutImmutable, method);
513 let static_name = self.ecx.ident_of(format!("__STATIC_METHOD_{}",
514 self.method_statics.len()));
515 let item = self.ecx.item(sp, static_name, self.static_attrs(), st);
516 self.method_statics.push(item);
517 self.ecx.expr_ident(sp, static_name)
520 /// Translate a `parse::Piece` to a static `rt::Piece`
521 fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr {
524 parse::String(s) => {
525 let s = token::intern_and_get_ident(s);
526 self.ecx.expr_call_global(sp,
527 self.rtpath("String"),
529 self.ecx.expr_str(sp, s)
532 parse::CurrentArgument => {
533 let nil = self.ecx.expr_lit(sp, ast::LitNil);
534 self.ecx.expr_call_global(sp, self.rtpath("CurrentArgument"), vec!(nil))
536 parse::Argument(ref arg) => {
537 // Translate the position
538 let pos = match arg.position {
539 // These two have a direct mapping
540 parse::ArgumentNext => {
541 let path = self.ecx.path_global(sp,
542 self.rtpath("ArgumentNext"));
543 self.ecx.expr_path(path)
545 parse::ArgumentIs(i) => {
546 self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"),
547 vec!(self.ecx.expr_uint(sp, i)))
549 // Named arguments are converted to positional arguments at
550 // the end of the list of arguments
551 parse::ArgumentNamed(n) => {
552 let i = match self.name_positions.find_equiv(&n) {
554 None => 0, // error already emitted elsewhere
556 let i = i + self.args.len();
557 self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"),
558 vec!(self.ecx.expr_uint(sp, i)))
562 // Translate the format
563 let fill = match arg.format.fill { Some(c) => c, None => ' ' };
564 let fill = self.ecx.expr_lit(sp, ast::LitChar(fill as u32));
565 let align = match arg.format.align {
566 parse::AlignLeft => {
567 self.ecx.path_global(sp, self.parsepath("AlignLeft"))
569 parse::AlignRight => {
570 self.ecx.path_global(sp, self.parsepath("AlignRight"))
572 parse::AlignUnknown => {
573 self.ecx.path_global(sp, self.parsepath("AlignUnknown"))
576 let align = self.ecx.expr_path(align);
577 let flags = self.ecx.expr_uint(sp, arg.format.flags);
578 let prec = self.trans_count(arg.format.precision);
579 let width = self.trans_count(arg.format.width);
580 let path = self.ecx.path_global(sp, self.rtpath("FormatSpec"));
581 let fmt = self.ecx.expr_struct(sp, path, vec!(
582 self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
583 self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
584 self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
585 self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
586 self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
588 // Translate the method (if any)
589 let method = match arg.method {
590 None => { self.none() }
592 let m = self.trans_method(*m);
593 self.some(self.ecx.expr_addr_of(sp, m))
596 let path = self.ecx.path_global(sp, self.rtpath("Argument"));
597 let s = self.ecx.expr_struct(sp, path, vec!(
598 self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
599 self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt),
600 self.ecx.field_imm(sp, self.ecx.ident_of("method"), method)));
601 self.ecx.expr_call_global(sp, self.rtpath("Argument"), vec!(s))
606 /// Actually builds the expression which the iformat! block will be expanded
608 fn to_expr(&self, extra: @ast::Expr) -> @ast::Expr {
609 let mut lets = Vec::new();
610 let mut locals = Vec::new();
611 let mut names = vec::from_fn(self.name_positions.len(), |_| None);
612 let mut pats = Vec::new();
613 let mut heads = Vec::new();
615 // First, declare all of our methods that are statics
616 for &method in self.method_statics.iter() {
617 let decl = respan(self.fmtsp, ast::DeclItem(method));
618 lets.push(@respan(self.fmtsp,
619 ast::StmtDecl(@decl, ast::DUMMY_NODE_ID)));
622 // Next, build up the static array which will become our precompiled
624 let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
625 let piece_ty = self.ecx.ty_path(self.ecx.path_all(
628 self.ecx.ident_of("std"),
629 self.ecx.ident_of("fmt"),
630 self.ecx.ident_of("rt"),
631 self.ecx.ident_of("Piece")),
633 self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("static").name)),
636 let ty = ast::TyFixedLengthVec(
638 self.ecx.expr_uint(self.fmtsp, self.pieces.len())
640 let ty = self.ecx.ty(self.fmtsp, ty);
641 let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
642 let static_name = self.ecx.ident_of("__STATIC_FMTSTR");
643 let item = self.ecx.item(self.fmtsp, static_name,
644 self.static_attrs(), st);
645 let decl = respan(self.fmtsp, ast::DeclItem(item));
646 lets.push(@respan(self.fmtsp, ast::StmtDecl(@decl, ast::DUMMY_NODE_ID)));
648 // Right now there is a bug such that for the expression:
650 // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
651 // vald for the call to `foo`. To work around this all arguments to the
652 // format! string are shoved into locals. Furthermore, we shove the address
653 // of each variable because we don't want to move out of the arguments
654 // passed to this function.
655 for (i, &e) in self.args.iter().enumerate() {
656 if self.arg_types[i].is_none() { continue } // error already generated
658 let name = self.ecx.ident_of(format!("__arg{}", i));
659 pats.push(self.ecx.pat_ident(e.span, name));
660 heads.push(self.ecx.expr_addr_of(e.span, e));
661 locals.push(self.format_arg(e.span, Exact(i),
662 self.ecx.expr_ident(e.span, name)));
664 for name in self.name_ordering.iter() {
665 let e = match self.names.find(name) {
666 Some(&e) if self.name_types.contains_key(name) => e,
667 Some(..) | None => continue
670 let lname = self.ecx.ident_of(format!("__arg{}", *name));
671 pats.push(self.ecx.pat_ident(e.span, lname));
672 heads.push(self.ecx.expr_addr_of(e.span, e));
673 names[*self.name_positions.get(name)] =
674 Some(self.format_arg(e.span,
675 Named((*name).clone()),
676 self.ecx.expr_ident(e.span, lname)));
679 // Now create a vector containing all the arguments
680 let slicename = self.ecx.ident_of("__args_vec");
682 let args = names.move_iter().map(|a| a.unwrap());
683 let mut args = locals.move_iter().chain(args);
684 let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
685 lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
688 // Now create the fmt::Arguments struct with all our locals we created.
689 let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
690 let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
691 let result = self.ecx.expr_call_global(self.fmtsp, vec!(
692 self.ecx.ident_of("std"),
693 self.ecx.ident_of("fmt"),
694 self.ecx.ident_of("Arguments"),
695 self.ecx.ident_of("new")), vec!(fmt, args_slice));
697 // We did all the work of making sure that the arguments
698 // structure is safe, so we can safely have an unsafe block.
699 let result = self.ecx.expr_block(P(ast::Block {
700 view_items: Vec::new(),
703 id: ast::DUMMY_NODE_ID,
704 rules: ast::UnsafeBlock(ast::CompilerGenerated),
707 let resname = self.ecx.ident_of("__args");
708 lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
709 let res = self.ecx.expr_ident(self.fmtsp, resname);
710 let result = self.ecx.expr_call(extra.span, extra, vec!(
711 self.ecx.expr_addr_of(extra.span, res)));
712 let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
715 // Constructs an AST equivalent to:
717 // match (&arg0, &arg1) {
718 // (tmp0, tmp1) => body
727 // Because of #11585 the new temporary lifetime rule, the enclosing
728 // statements for these temporaries become the let's themselves.
729 // If one or more of them are RefCell's, RefCell borrow() will also
730 // end there; they don't last long enough for body to use them. The
731 // match expression solves the scope problem.
733 // Note, it may also very well be transformed to:
738 // ref tmp1 => body } } }
740 // But the nested match expression is proved to perform not as well
741 // as series of let's; the first approach does.
742 let pat = self.ecx.pat(self.fmtsp, ast::PatTup(pats));
743 let arm = self.ecx.arm(self.fmtsp, vec!(pat), body);
744 let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
745 self.ecx.expr_match(self.fmtsp, head, vec!(arm))
748 fn format_arg(&self, sp: Span, argno: Position, arg: @ast::Expr)
750 let ty = match argno {
751 Exact(ref i) => self.arg_types[*i].get_ref(),
752 Named(ref s) => self.name_types.get(s)
755 let fmt_fn = match *ty {
756 Known(ref tyname) => {
757 match tyname.as_slice() {
759 "?" => "secret_poly",
760 "b" => "secret_bool",
761 "c" => "secret_char",
762 "d" | "i" => "secret_signed",
763 "e" => "secret_lower_exp",
764 "E" => "secret_upper_exp",
765 "f" => "secret_float",
766 "o" => "secret_octal",
767 "p" => "secret_pointer",
768 "s" => "secret_string",
769 "t" => "secret_binary",
770 "u" => "secret_unsigned",
771 "x" => "secret_lower_hex",
772 "X" => "secret_upper_hex",
774 self.ecx.span_err(sp, format!("unknown format trait `{}`",
781 return self.ecx.expr_call_global(sp, vec!(
782 self.ecx.ident_of("std"),
783 self.ecx.ident_of("fmt"),
784 self.ecx.ident_of("argumentstr")), vec!(arg))
787 return self.ecx.expr_call_global(sp, vec!(
788 self.ecx.ident_of("std"),
789 self.ecx.ident_of("fmt"),
790 self.ecx.ident_of("argumentuint")), vec!(arg))
794 let format_fn = self.ecx.path_global(sp, vec!(
795 self.ecx.ident_of("std"),
796 self.ecx.ident_of("fmt"),
797 self.ecx.ident_of(fmt_fn)));
798 self.ecx.expr_call_global(sp, vec!(
799 self.ecx.ident_of("std"),
800 self.ecx.ident_of("fmt"),
801 self.ecx.ident_of("argument")), vec!(self.ecx.expr_path(format_fn), arg))
805 pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
806 tts: &[ast::TokenTree]) -> base::MacResult {
808 match parse_args(ecx, sp, tts) {
809 (extra, Some((efmt, args, order, names))) => {
810 MRExpr(expand_preparsed_format_args(ecx, sp, extra, efmt, args,
813 (_, None) => MRExpr(ecx.expr_uint(sp, 2))
817 /// Take the various parts of `format_args!(extra, efmt, args...,
818 /// name=names...)` and construct the appropriate formatting
820 pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
822 efmt: @ast::Expr, args: Vec<@ast::Expr>,
823 name_ordering: Vec<~str>,
824 names: HashMap<~str, @ast::Expr>) -> @ast::Expr {
825 let arg_types = vec::from_fn(args.len(), |_| None);
826 let mut cx = Context {
829 arg_types: arg_types,
831 name_positions: HashMap::new(),
832 name_types: HashMap::new(),
833 name_ordering: name_ordering,
837 method_statics: Vec::new(),
840 cx.fmtsp = efmt.span;
841 // Be sure to recursively expand macros just in case the format string uses
842 // a macro to build the format expression.
843 let expr = cx.ecx.expand_expr(efmt);
844 let fmt = match expr_to_str(cx.ecx,
846 "format argument must be a string literal.") {
847 Some((fmt, _)) => fmt,
848 None => return MacResult::raw_dummy_expr(sp)
851 let mut parser = parse::Parser::new(fmt.get());
853 match parser.next() {
855 if parser.errors.len() > 0 { break }
856 cx.verify_piece(&piece);
857 let piece = cx.trans_piece(&piece);
858 cx.pieces.push(piece);
863 match parser.errors.shift() {
865 cx.ecx.span_err(efmt.span, "invalid format string: " + error);
866 return MacResult::raw_dummy_expr(sp);
871 // Make sure that all arguments were used and all arguments have types.
872 for (i, ty) in cx.arg_types.iter().enumerate() {
874 cx.ecx.span_err(cx.args[i].span, "argument never used");
877 for (name, e) in cx.names.iter() {
878 if !cx.name_types.contains_key(name) {
879 cx.ecx.span_err(e.span, "named argument never used");