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;
20 use parse = fmt_macros;
21 use std::collections::HashMap;
22 use std::gc::{Gc, GC};
24 #[deriving(PartialEq)]
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<Gc<ast::Expr>>,
43 arg_types: Vec<Option<ArgumentType>>,
44 /// Parsed named expressions and the types that we've found for them so far.
45 /// Note that we keep a side-array of the ordering of the named arguments
46 /// found to be sure that we can translate them in the same order that they
48 names: HashMap<String, Gc<ast::Expr>>,
49 name_types: HashMap<String, ArgumentType>,
50 name_ordering: Vec<String>,
52 /// The latest consecutive literal strings
53 literal: Option<String>,
55 /// Collection of the compiled `rt::Piece` structures
56 pieces: Vec<Gc<ast::Expr>>,
57 name_positions: HashMap<String, uint>,
58 method_statics: Vec<Gc<ast::Item>>,
60 /// Updated as arguments are consumed or methods are entered
67 MethodCall(Gc<ast::Expr>, ast::Ident),
70 /// Parses the arguments from the given list of tokens, returning None
71 /// if there's a parse error so we can continue parsing other format!
74 /// If parsing succeeds, the second return value is:
76 /// Some((fmtstr, unnamed arguments, ordering of named arguments,
78 fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
79 tts: &[ast::TokenTree])
80 -> (Invocation, Option<(Gc<ast::Expr>, Vec<Gc<ast::Expr>>, Vec<String>,
81 HashMap<String, Gc<ast::Expr>>)>) {
82 let mut args = Vec::new();
83 let mut names = HashMap::<String, Gc<ast::Expr>>::new();
84 let mut order = Vec::new();
86 let mut p = ecx.new_parser_from_tts(tts);
87 // Parse the leading function expression (maybe a block, maybe a path)
88 let invocation = if allow_method {
89 let e = p.parse_expr();
90 if !p.eat(&token::COMMA) {
91 ecx.span_err(sp, "expected token: `,`");
92 return (Call(e), None);
94 MethodCall(e, p.parse_ident())
98 if !p.eat(&token::COMMA) {
99 ecx.span_err(sp, "expected token: `,`");
100 return (invocation, None);
103 if p.token == token::EOF {
104 ecx.span_err(sp, "requires at least a format string argument");
105 return (invocation, None);
107 let fmtstr = p.parse_expr();
108 let mut named = false;
109 while p.token != token::EOF {
110 if !p.eat(&token::COMMA) {
111 ecx.span_err(sp, "expected token: `,`");
112 return (invocation, None);
114 if p.token == token::EOF { break } // accept trailing commas
115 if named || (token::is_ident(&p.token) &&
116 p.look_ahead(1, |t| *t == token::EQ)) {
118 let ident = match p.token {
119 token::IDENT(i, _) => {
125 "expected ident, positional arguments \
126 cannot follow named arguments");
127 return (invocation, None);
131 format!("expected ident for named argument, but found `{}`",
132 p.this_token_to_string()).as_slice());
133 return (invocation, None);
136 let interned_name = token::get_ident(ident);
137 let name = interned_name.get();
138 p.expect(&token::EQ);
139 let e = p.parse_expr();
140 match names.find_equiv(&name) {
144 format!("duplicate argument named `{}`",
146 ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
150 order.push(name.to_string());
151 names.insert(name.to_string(), e);
153 args.push(p.parse_expr());
156 return (invocation, Some((fmtstr, args, order, names)));
159 impl<'a, 'b> Context<'a, 'b> {
160 /// Verifies one piece of a parse string. All errors are not emitted as
161 /// fatal so we can continue giving errors about this and possibly other
163 fn verify_piece(&mut self, p: &parse::Piece) {
165 parse::String(..) => {}
166 parse::Argument(ref arg) => {
167 // width/precision first, if they have implicit positional
168 // parameters it makes more sense to consume them first.
169 self.verify_count(arg.format.width);
170 self.verify_count(arg.format.precision);
172 // argument second, if it's an implicit positional parameter
173 // it's written second, so it should come after width/precision.
174 let pos = match arg.position {
175 parse::ArgumentNext => {
176 let i = self.next_arg;
177 if self.check_positional_ok() {
182 parse::ArgumentIs(i) => Exact(i),
183 parse::ArgumentNamed(s) => Named(s.to_string()),
186 let ty = Known(arg.format.ty.to_string());
187 self.verify_arg_type(pos, ty);
192 fn verify_count(&mut self, c: parse::Count) {
194 parse::CountImplied | parse::CountIs(..) => {}
195 parse::CountIsParam(i) => {
196 self.verify_arg_type(Exact(i), Unsigned);
198 parse::CountIsName(s) => {
199 self.verify_arg_type(Named(s.to_string()), Unsigned);
201 parse::CountIsNextParam => {
202 if self.check_positional_ok() {
203 let next_arg = self.next_arg;
204 self.verify_arg_type(Exact(next_arg), Unsigned);
211 fn check_positional_ok(&mut self) -> bool {
212 if self.nest_level != 0 {
213 self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
214 arguments nested inside methods");
221 fn describe_num_args(&self) -> String {
222 match self.args.len() {
223 0 => "no arguments given".to_string(),
224 1 => "there is 1 argument".to_string(),
225 x => format!("there are {} arguments", x),
229 fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
232 if self.args.len() <= arg {
233 let msg = format!("invalid reference to argument `{}` ({:s})",
234 arg, self.describe_num_args());
236 self.ecx.span_err(self.fmtsp, msg.as_slice());
240 let arg_type = match self.arg_types.get(arg) {
242 &Some(ref x) => Some(x)
244 self.verify_same(self.args.get(arg).span, &ty, arg_type);
246 if self.arg_types.get(arg).is_none() {
247 *self.arg_types.get_mut(arg) = Some(ty);
252 let span = match self.names.find(&name) {
255 let msg = format!("there is no argument named `{}`", name);
256 self.ecx.span_err(self.fmtsp, msg.as_slice());
260 self.verify_same(span, &ty, self.name_types.find(&name));
261 if !self.name_types.contains_key(&name) {
262 self.name_types.insert(name.clone(), ty);
264 // Assign this named argument a slot in the arguments array if
265 // it hasn't already been assigned a slot.
266 if !self.name_positions.contains_key(&name) {
267 let slot = self.name_positions.len();
268 self.name_positions.insert(name, slot);
274 /// When we're keeping track of the types that are declared for certain
275 /// arguments, we assume that `None` means we haven't seen this argument
276 /// yet, `Some(None)` means that we've seen the argument, but no format was
277 /// specified, and `Some(Some(x))` means that the argument was declared to
280 /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
281 /// that: `Some(None) == Some(Some(x))`
282 fn verify_same(&self,
285 before: Option<&ArgumentType>) {
286 let cur = match before {
294 (&Known(ref cur), &Known(ref ty)) => {
295 self.ecx.span_err(sp,
296 format!("argument redeclared with type `{}` when \
297 it was previously `{}`",
301 (&Known(ref cur), _) => {
302 self.ecx.span_err(sp,
303 format!("argument used to format with `{}` was \
304 attempted to not be used for formatting",
307 (_, &Known(ref ty)) => {
308 self.ecx.span_err(sp,
309 format!("argument previously used as a format \
310 argument attempted to be used as `{}`",
314 self.ecx.span_err(sp, "argument declared with multiple formats");
319 /// These attributes are applied to all statics that this syntax extension
321 fn static_attrs(&self) -> Vec<ast::Attribute> {
322 // Flag statics as `inline` so LLVM can merge duplicate globals as much
323 // as possible (which we're generating a whole lot of).
324 let unnamed = self.ecx.meta_word(self.fmtsp, InternedString::new("inline"));
325 let unnamed = self.ecx.attribute(self.fmtsp, unnamed);
327 // Do not warn format string as dead code
328 let dead_code = self.ecx.meta_word(self.fmtsp,
329 InternedString::new("dead_code"));
330 let allow_dead_code = self.ecx.meta_list(self.fmtsp,
331 InternedString::new("allow"),
333 let allow_dead_code = self.ecx.attribute(self.fmtsp, allow_dead_code);
334 return vec!(unnamed, allow_dead_code);
337 fn rtpath(&self, s: &str) -> Vec<ast::Ident> {
338 vec!(self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
339 self.ecx.ident_of("rt"), self.ecx.ident_of(s))
342 fn trans_count(&self, c: parse::Count) -> Gc<ast::Expr> {
345 parse::CountIs(i) => {
346 self.ecx.expr_call_global(sp, self.rtpath("CountIs"),
347 vec!(self.ecx.expr_uint(sp, i)))
349 parse::CountIsParam(i) => {
350 self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"),
351 vec!(self.ecx.expr_uint(sp, i)))
353 parse::CountImplied => {
354 let path = self.ecx.path_global(sp, self.rtpath("CountImplied"));
355 self.ecx.expr_path(path)
357 parse::CountIsNextParam => {
358 let path = self.ecx.path_global(sp, self.rtpath("CountIsNextParam"));
359 self.ecx.expr_path(path)
361 parse::CountIsName(n) => {
362 let i = match self.name_positions.find_equiv(&n) {
364 None => 0, // error already emitted elsewhere
366 let i = i + self.args.len();
367 self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"),
368 vec!(self.ecx.expr_uint(sp, i)))
373 /// Translate the accumulated string literals to a static `rt::Piece`
374 fn trans_literal_string(&mut self) -> Option<Gc<ast::Expr>> {
376 self.literal.take().map(|s| {
377 let s = token::intern_and_get_ident(s.as_slice());
378 self.ecx.expr_call_global(sp,
379 self.rtpath("String"),
381 self.ecx.expr_str(sp, s)
386 /// Translate a `parse::Piece` to a static `rt::Piece`
387 fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> {
390 parse::String(s) => {
392 Some(ref mut sb) => sb.push_str(s),
393 ref mut empty => *empty = Some(String::from_str(s)),
397 parse::Argument(ref arg) => {
398 // Translate the position
399 let pos = match arg.position {
400 // These two have a direct mapping
401 parse::ArgumentNext => {
402 let path = self.ecx.path_global(sp,
403 self.rtpath("ArgumentNext"));
404 self.ecx.expr_path(path)
406 parse::ArgumentIs(i) => {
407 self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"),
408 vec!(self.ecx.expr_uint(sp, i)))
410 // Named arguments are converted to positional arguments at
411 // the end of the list of arguments
412 parse::ArgumentNamed(n) => {
413 let i = match self.name_positions.find_equiv(&n) {
415 None => 0, // error already emitted elsewhere
417 let i = i + self.args.len();
418 self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"),
419 vec!(self.ecx.expr_uint(sp, i)))
423 // Translate the format
424 let fill = match arg.format.fill { Some(c) => c, None => ' ' };
425 let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
426 let align = match arg.format.align {
427 parse::AlignLeft => {
428 self.ecx.path_global(sp, self.rtpath("AlignLeft"))
430 parse::AlignRight => {
431 self.ecx.path_global(sp, self.rtpath("AlignRight"))
433 parse::AlignUnknown => {
434 self.ecx.path_global(sp, self.rtpath("AlignUnknown"))
437 let align = self.ecx.expr_path(align);
438 let flags = self.ecx.expr_uint(sp, arg.format.flags);
439 let prec = self.trans_count(arg.format.precision);
440 let width = self.trans_count(arg.format.width);
441 let path = self.ecx.path_global(sp, self.rtpath("FormatSpec"));
442 let fmt = self.ecx.expr_struct(sp, path, vec!(
443 self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
444 self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
445 self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
446 self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
447 self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
449 let path = self.ecx.path_global(sp, self.rtpath("Argument"));
450 let s = self.ecx.expr_struct(sp, path, vec!(
451 self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
452 self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt)));
453 Some(self.ecx.expr_call_global(sp, self.rtpath("Argument"), vec!(s)))
458 /// Actually builds the expression which the iformat! block will be expanded
460 fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> {
461 let mut lets = Vec::new();
462 let mut locals = Vec::new();
463 let mut names = Vec::from_fn(self.name_positions.len(), |_| None);
464 let mut pats = Vec::new();
465 let mut heads = Vec::new();
467 // First, declare all of our methods that are statics
468 for &method in self.method_statics.iter() {
469 let decl = respan(self.fmtsp, ast::DeclItem(method));
470 lets.push(box(GC) respan(self.fmtsp,
471 ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
474 // Next, build up the static array which will become our precompiled
476 let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
477 let piece_ty = self.ecx.ty_path(self.ecx.path_all(
480 self.ecx.ident_of("std"),
481 self.ecx.ident_of("fmt"),
482 self.ecx.ident_of("rt"),
483 self.ecx.ident_of("Piece")),
484 vec!(self.ecx.lifetime(self.fmtsp,
485 self.ecx.ident_of("'static").name)),
488 let ty = ast::TyFixedLengthVec(
490 self.ecx.expr_uint(self.fmtsp, self.pieces.len())
492 let ty = self.ecx.ty(self.fmtsp, ty);
493 let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
494 let static_name = self.ecx.ident_of("__STATIC_FMTSTR");
495 let item = self.ecx.item(self.fmtsp, static_name,
496 self.static_attrs(), st);
497 let decl = respan(self.fmtsp, ast::DeclItem(item));
498 lets.push(box(GC) respan(self.fmtsp,
499 ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
501 // Right now there is a bug such that for the expression:
503 // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
504 // vald for the call to `foo`. To work around this all arguments to the
505 // format! string are shoved into locals. Furthermore, we shove the address
506 // of each variable because we don't want to move out of the arguments
507 // passed to this function.
508 for (i, &e) in self.args.iter().enumerate() {
509 if self.arg_types.get(i).is_none() {
510 continue // error already generated
513 let name = self.ecx.ident_of(format!("__arg{}", i).as_slice());
514 pats.push(self.ecx.pat_ident(e.span, name));
515 heads.push(self.ecx.expr_addr_of(e.span, e));
516 locals.push(self.format_arg(e.span, Exact(i),
517 self.ecx.expr_ident(e.span, name)));
519 for name in self.name_ordering.iter() {
520 let e = match self.names.find(name) {
521 Some(&e) if self.name_types.contains_key(name) => e,
522 Some(..) | None => continue
525 let lname = self.ecx.ident_of(format!("__arg{}",
527 pats.push(self.ecx.pat_ident(e.span, lname));
528 heads.push(self.ecx.expr_addr_of(e.span, e));
529 *names.get_mut(*self.name_positions.get(name)) =
530 Some(self.format_arg(e.span,
531 Named((*name).clone()),
532 self.ecx.expr_ident(e.span, lname)));
535 // Now create a vector containing all the arguments
536 let slicename = self.ecx.ident_of("__args_vec");
538 let args = names.move_iter().map(|a| a.unwrap());
539 let mut args = locals.move_iter().chain(args);
540 let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
541 lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
544 // Now create the fmt::Arguments struct with all our locals we created.
545 let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
546 let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
547 let result = self.ecx.expr_call_global(self.fmtsp, vec!(
548 self.ecx.ident_of("std"),
549 self.ecx.ident_of("fmt"),
550 self.ecx.ident_of("Arguments"),
551 self.ecx.ident_of("new")), vec!(fmt, args_slice));
553 // We did all the work of making sure that the arguments
554 // structure is safe, so we can safely have an unsafe block.
555 let result = self.ecx.expr_block(P(ast::Block {
556 view_items: Vec::new(),
559 id: ast::DUMMY_NODE_ID,
560 rules: ast::UnsafeBlock(ast::CompilerGenerated),
563 let resname = self.ecx.ident_of("__args");
564 lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
565 let res = self.ecx.expr_ident(self.fmtsp, resname);
566 let result = match invocation {
568 self.ecx.expr_call(e.span, e,
569 vec!(self.ecx.expr_addr_of(e.span, res)))
571 MethodCall(e, m) => {
572 self.ecx.expr_method_call(e.span, e, m,
573 vec!(self.ecx.expr_addr_of(e.span, res)))
576 let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
579 // Constructs an AST equivalent to:
581 // match (&arg0, &arg1) {
582 // (tmp0, tmp1) => body
591 // Because of #11585 the new temporary lifetime rule, the enclosing
592 // statements for these temporaries become the let's themselves.
593 // If one or more of them are RefCell's, RefCell borrow() will also
594 // end there; they don't last long enough for body to use them. The
595 // match expression solves the scope problem.
597 // Note, it may also very well be transformed to:
602 // ref tmp1 => body } } }
604 // But the nested match expression is proved to perform not as well
605 // as series of let's; the first approach does.
606 let pat = self.ecx.pat(self.fmtsp, ast::PatTup(pats));
607 let arm = self.ecx.arm(self.fmtsp, vec!(pat), body);
608 let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
609 self.ecx.expr_match(self.fmtsp, head, vec!(arm))
612 fn format_arg(&self, sp: Span, argno: Position, arg: Gc<ast::Expr>)
614 let ty = match argno {
615 Exact(ref i) => self.arg_types.get(*i).get_ref(),
616 Named(ref s) => self.name_types.get(s)
619 let (krate, fmt_fn) = match *ty {
620 Known(ref tyname) => {
621 match tyname.as_slice() {
622 "" => ("std", "secret_show"),
623 "?" => ("debug", "secret_poly"),
624 "b" => ("std", "secret_bool"),
625 "c" => ("std", "secret_char"),
626 "d" | "i" => ("std", "secret_signed"),
627 "e" => ("std", "secret_lower_exp"),
628 "E" => ("std", "secret_upper_exp"),
629 "f" => ("std", "secret_float"),
630 "o" => ("std", "secret_octal"),
631 "p" => ("std", "secret_pointer"),
632 "s" => ("std", "secret_string"),
633 "t" => ("std", "secret_binary"),
634 "u" => ("std", "secret_unsigned"),
635 "x" => ("std", "secret_lower_hex"),
636 "X" => ("std", "secret_upper_hex"),
640 format!("unknown format trait `{}`",
641 *tyname).as_slice());
647 return self.ecx.expr_call_global(sp, vec!(
648 self.ecx.ident_of("std"),
649 self.ecx.ident_of("fmt"),
650 self.ecx.ident_of("argumentstr")), vec!(arg))
653 return self.ecx.expr_call_global(sp, vec!(
654 self.ecx.ident_of("std"),
655 self.ecx.ident_of("fmt"),
656 self.ecx.ident_of("argumentuint")), vec!(arg))
660 let format_fn = self.ecx.path_global(sp, vec!(
661 self.ecx.ident_of(krate),
662 self.ecx.ident_of("fmt"),
663 self.ecx.ident_of(fmt_fn)));
664 self.ecx.expr_call_global(sp, vec!(
665 self.ecx.ident_of("std"),
666 self.ecx.ident_of("fmt"),
667 self.ecx.ident_of("argument")), vec!(self.ecx.expr_path(format_fn), arg))
671 pub fn expand_format_args(ecx: &mut ExtCtxt, sp: Span,
672 tts: &[ast::TokenTree]) -> Box<base::MacResult> {
674 match parse_args(ecx, sp, false, tts) {
675 (invocation, Some((efmt, args, order, names))) => {
676 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
679 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
683 pub fn expand_format_args_method(ecx: &mut ExtCtxt, sp: Span,
684 tts: &[ast::TokenTree]) -> Box<base::MacResult> {
686 match parse_args(ecx, sp, true, tts) {
687 (invocation, Some((efmt, args, order, names))) => {
688 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
691 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
695 /// Take the various parts of `format_args!(extra, efmt, args...,
696 /// name=names...)` and construct the appropriate formatting
698 pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
699 invocation: Invocation,
701 args: Vec<Gc<ast::Expr>>,
702 name_ordering: Vec<String>,
703 names: HashMap<String, Gc<ast::Expr>>)
706 let arg_types = Vec::from_fn(args.len(), |_| None);
707 let mut cx = Context {
710 arg_types: arg_types,
712 name_positions: HashMap::new(),
713 name_types: HashMap::new(),
714 name_ordering: name_ordering,
719 method_statics: Vec::new(),
722 cx.fmtsp = efmt.span;
723 let fmt = match expr_to_string(cx.ecx,
725 "format argument must be a string literal.") {
726 Some((fmt, _)) => fmt,
727 None => return DummyResult::raw_expr(sp)
730 let mut parser = parse::Parser::new(fmt.get());
732 match parser.next() {
734 if parser.errors.len() > 0 { break }
735 cx.verify_piece(&piece);
736 match cx.trans_piece(&piece) {
738 cx.trans_literal_string().map(|piece|
739 cx.pieces.push(piece));
740 cx.pieces.push(piece);
748 match parser.errors.shift() {
750 cx.ecx.span_err(efmt.span,
751 format!("invalid format string: {}",
753 return DummyResult::raw_expr(sp);
757 cx.trans_literal_string().map(|piece| cx.pieces.push(piece));
759 // Make sure that all arguments were used and all arguments have types.
760 for (i, ty) in cx.arg_types.iter().enumerate() {
762 cx.ecx.span_err(cx.args.get(i).span, "argument never used");
765 for (name, e) in cx.names.iter() {
766 if !cx.name_types.contains_key(name) {
767 cx.ecx.span_err(e.span, "named argument never used");
771 cx.to_expr(invocation)