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.
12 use codemap::{Span, respan};
15 use ext::build::AstBuilder;
16 use fmt_macros as parse;
17 use parse::token::InternedString;
21 use std::collections::HashMap;
23 #[deriving(PartialEq)]
35 struct Context<'a, 'b:'a> {
36 ecx: &'a mut ExtCtxt<'b>,
39 /// Parsed argument expressions and the types that we've found so far for
41 args: Vec<P<ast::Expr>>,
42 arg_types: Vec<Option<ArgumentType>>,
43 /// Parsed named expressions and the types that we've found for them so far.
44 /// Note that we keep a side-array of the ordering of the named arguments
45 /// found to be sure that we can translate them in the same order that they
47 names: HashMap<String, P<ast::Expr>>,
48 name_types: HashMap<String, ArgumentType>,
49 name_ordering: Vec<String>,
51 /// The latest consecutive literal strings, or empty if there weren't any.
54 /// Collection of the compiled `rt::Argument` structures
55 pieces: Vec<P<ast::Expr>>,
56 /// Collection of string literals
57 str_pieces: Vec<P<ast::Expr>>,
58 /// Stays `true` if all formatting parameters are default (as in "{}{}").
59 all_pieces_simple: bool,
61 name_positions: HashMap<String, uint>,
62 method_statics: Vec<P<ast::Item>>,
64 /// Updated as arguments are consumed or methods are entered
71 MethodCall(P<ast::Expr>, ast::Ident),
74 /// Parses the arguments from the given list of tokens, returning None
75 /// if there's a parse error so we can continue parsing other format!
78 /// If parsing succeeds, the second return value is:
80 /// Some((fmtstr, unnamed arguments, ordering of named arguments,
82 fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
83 tts: &[ast::TokenTree])
84 -> (Invocation, Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<String>,
85 HashMap<String, P<ast::Expr>>)>) {
86 let mut args = Vec::new();
87 let mut names = HashMap::<String, P<ast::Expr>>::new();
88 let mut order = Vec::new();
90 let mut p = ecx.new_parser_from_tts(tts);
91 // Parse the leading function expression (maybe a block, maybe a path)
92 let invocation = if allow_method {
93 let e = p.parse_expr();
94 if !p.eat(&token::COMMA) {
95 ecx.span_err(sp, "expected token: `,`");
96 return (Call(e), None);
98 MethodCall(e, p.parse_ident())
102 if !p.eat(&token::COMMA) {
103 ecx.span_err(sp, "expected token: `,`");
104 return (invocation, None);
107 if p.token == token::EOF {
108 ecx.span_err(sp, "requires at least a format string argument");
109 return (invocation, None);
111 let fmtstr = p.parse_expr();
112 let mut named = false;
113 while p.token != token::EOF {
114 if !p.eat(&token::COMMA) {
115 ecx.span_err(sp, "expected token: `,`");
116 return (invocation, None);
118 if p.token == token::EOF { break } // accept trailing commas
119 if named || (token::is_ident(&p.token) &&
120 p.look_ahead(1, |t| *t == token::EQ)) {
122 let ident = match p.token {
123 token::IDENT(i, _) => {
129 "expected ident, positional arguments \
130 cannot follow named arguments");
131 return (invocation, None);
135 format!("expected ident for named argument, found `{}`",
136 p.this_token_to_string()).as_slice());
137 return (invocation, None);
140 let interned_name = token::get_ident(ident);
141 let name = interned_name.get();
142 p.expect(&token::EQ);
143 let e = p.parse_expr();
144 match names.find_equiv(&name) {
148 format!("duplicate argument named `{}`",
150 ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
154 order.push(name.to_string());
155 names.insert(name.to_string(), e);
157 args.push(p.parse_expr());
160 return (invocation, Some((fmtstr, args, order, names)));
163 impl<'a, 'b> Context<'a, 'b> {
164 /// Verifies one piece of a parse string. All errors are not emitted as
165 /// fatal so we can continue giving errors about this and possibly other
167 fn verify_piece(&mut self, p: &parse::Piece) {
169 parse::String(..) => {}
170 parse::Argument(ref arg) => {
171 // width/precision first, if they have implicit positional
172 // parameters it makes more sense to consume them first.
173 self.verify_count(arg.format.width);
174 self.verify_count(arg.format.precision);
176 // argument second, if it's an implicit positional parameter
177 // it's written second, so it should come after width/precision.
178 let pos = match arg.position {
179 parse::ArgumentNext => {
180 let i = self.next_arg;
181 if self.check_positional_ok() {
186 parse::ArgumentIs(i) => Exact(i),
187 parse::ArgumentNamed(s) => Named(s.to_string()),
190 let ty = Known(arg.format.ty.to_string());
191 self.verify_arg_type(pos, ty);
196 fn verify_count(&mut self, c: parse::Count) {
198 parse::CountImplied | parse::CountIs(..) => {}
199 parse::CountIsParam(i) => {
200 self.verify_arg_type(Exact(i), Unsigned);
202 parse::CountIsName(s) => {
203 self.verify_arg_type(Named(s.to_string()), Unsigned);
205 parse::CountIsNextParam => {
206 if self.check_positional_ok() {
207 let next_arg = self.next_arg;
208 self.verify_arg_type(Exact(next_arg), Unsigned);
215 fn check_positional_ok(&mut self) -> bool {
216 if self.nest_level != 0 {
217 self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
218 arguments nested inside methods");
225 fn describe_num_args(&self) -> String {
226 match self.args.len() {
227 0 => "no arguments given".to_string(),
228 1 => "there is 1 argument".to_string(),
229 x => format!("there are {} arguments", x),
233 fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
236 if self.args.len() <= arg {
237 let msg = format!("invalid reference to argument `{}` ({:s})",
238 arg, self.describe_num_args());
240 self.ecx.span_err(self.fmtsp, msg.as_slice());
244 let arg_type = match self.arg_types.get(arg) {
246 &Some(ref x) => Some(x)
248 self.verify_same(self.args.get(arg).span, &ty, arg_type);
250 if self.arg_types.get(arg).is_none() {
251 *self.arg_types.get_mut(arg) = Some(ty);
256 let span = match self.names.find(&name) {
259 let msg = format!("there is no argument named `{}`", name);
260 self.ecx.span_err(self.fmtsp, msg.as_slice());
264 self.verify_same(span, &ty, self.name_types.find(&name));
265 if !self.name_types.contains_key(&name) {
266 self.name_types.insert(name.clone(), ty);
268 // Assign this named argument a slot in the arguments array if
269 // it hasn't already been assigned a slot.
270 if !self.name_positions.contains_key(&name) {
271 let slot = self.name_positions.len();
272 self.name_positions.insert(name, slot);
278 /// When we're keeping track of the types that are declared for certain
279 /// arguments, we assume that `None` means we haven't seen this argument
280 /// yet, `Some(None)` means that we've seen the argument, but no format was
281 /// specified, and `Some(Some(x))` means that the argument was declared to
284 /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
285 /// that: `Some(None) == Some(Some(x))`
286 fn verify_same(&self,
289 before: Option<&ArgumentType>) {
290 let cur = match before {
298 (&Known(ref cur), &Known(ref ty)) => {
299 self.ecx.span_err(sp,
300 format!("argument redeclared with type `{}` when \
301 it was previously `{}`",
305 (&Known(ref cur), _) => {
306 self.ecx.span_err(sp,
307 format!("argument used to format with `{}` was \
308 attempted to not be used for formatting",
311 (_, &Known(ref ty)) => {
312 self.ecx.span_err(sp,
313 format!("argument previously used as a format \
314 argument attempted to be used as `{}`",
318 self.ecx.span_err(sp, "argument declared with multiple formats");
323 /// These attributes are applied to all statics that this syntax extension
325 fn static_attrs(ecx: &ExtCtxt, fmtsp: Span) -> Vec<ast::Attribute> {
326 // Flag statics as `inline` so LLVM can merge duplicate globals as much
327 // as possible (which we're generating a whole lot of).
328 let unnamed = ecx.meta_word(fmtsp, InternedString::new("inline"));
329 let unnamed = ecx.attribute(fmtsp, unnamed);
331 // Do not warn format string as dead code
332 let dead_code = ecx.meta_word(fmtsp, InternedString::new("dead_code"));
333 let allow_dead_code = ecx.meta_list(fmtsp,
334 InternedString::new("allow"),
336 let allow_dead_code = ecx.attribute(fmtsp, allow_dead_code);
337 vec![unnamed, allow_dead_code]
340 fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
341 vec![ecx.ident_of("std"), ecx.ident_of("fmt"), ecx.ident_of("rt"), ecx.ident_of(s)]
344 fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
347 parse::CountIs(i) => {
348 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIs"),
349 vec!(self.ecx.expr_uint(sp, i)))
351 parse::CountIsParam(i) => {
352 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIsParam"),
353 vec!(self.ecx.expr_uint(sp, i)))
355 parse::CountImplied => {
356 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
358 self.ecx.expr_path(path)
360 parse::CountIsNextParam => {
361 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
362 "CountIsNextParam"));
363 self.ecx.expr_path(path)
365 parse::CountIsName(n) => {
366 let i = match self.name_positions.find_equiv(&n) {
368 None => 0, // error already emitted elsewhere
370 let i = i + self.args.len();
371 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIsParam"),
372 vec!(self.ecx.expr_uint(sp, i)))
377 /// Translate the accumulated string literals to a literal expression
378 fn trans_literal_string(&mut self) -> P<ast::Expr> {
380 let s = token::intern_and_get_ident(self.literal.as_slice());
381 self.literal.clear();
382 self.ecx.expr_str(sp, s)
385 /// Translate a `parse::Piece` to a static `rt::Argument` or append
386 /// to the `literal` string.
387 fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> {
390 parse::String(s) => {
391 self.literal.push_str(s);
394 parse::Argument(ref arg) => {
395 // Translate the position
396 let pos = match arg.position {
397 // These two have a direct mapping
398 parse::ArgumentNext => {
399 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
401 self.ecx.expr_path(path)
403 parse::ArgumentIs(i) => {
404 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "ArgumentIs"),
405 vec!(self.ecx.expr_uint(sp, i)))
407 // Named arguments are converted to positional arguments at
408 // the end of the list of arguments
409 parse::ArgumentNamed(n) => {
410 let i = match self.name_positions.find_equiv(&n) {
412 None => 0, // error already emitted elsewhere
414 let i = i + self.args.len();
415 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "ArgumentIs"),
416 vec!(self.ecx.expr_uint(sp, i)))
420 let simple_arg = parse::Argument {
421 position: parse::ArgumentNext,
422 format: parse::FormatSpec {
423 fill: arg.format.fill,
424 align: parse::AlignUnknown,
426 precision: parse::CountImplied,
427 width: parse::CountImplied,
432 let fill = match arg.format.fill { Some(c) => c, None => ' ' };
434 if *arg != simple_arg || fill != ' ' {
435 self.all_pieces_simple = false;
438 // Translate the format
439 let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
440 let align = match arg.format.align {
441 parse::AlignLeft => {
442 self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignLeft"))
444 parse::AlignRight => {
445 self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignRight"))
447 parse::AlignCenter => {
448 self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignCenter"))
450 parse::AlignUnknown => {
451 self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignUnknown"))
454 let align = self.ecx.expr_path(align);
455 let flags = self.ecx.expr_uint(sp, arg.format.flags);
456 let prec = self.trans_count(arg.format.precision);
457 let width = self.trans_count(arg.format.width);
458 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
459 let fmt = self.ecx.expr_struct(sp, path, vec!(
460 self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
461 self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
462 self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
463 self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
464 self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
466 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument"));
467 Some(self.ecx.expr_struct(sp, path, vec!(
468 self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
469 self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))))
474 fn item_static_array(ecx: &mut ExtCtxt,
476 piece_ty: P<ast::Ty>,
477 pieces: Vec<P<ast::Expr>>)
479 let fmtsp = piece_ty.span;
480 let pieces_len = ecx.expr_uint(fmtsp, pieces.len());
481 let fmt = ecx.expr_vec(fmtsp, pieces);
482 let ty = ast::TyFixedLengthVec(
486 let ty = ecx.ty(fmtsp, ty);
487 let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
488 let item = ecx.item(fmtsp, name, Context::static_attrs(ecx, fmtsp), st);
489 let decl = respan(fmtsp, ast::DeclItem(item));
490 P(respan(fmtsp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
493 /// Actually builds the expression which the iformat! block will be expanded
495 fn to_expr(mut self, invocation: Invocation) -> P<ast::Expr> {
496 let mut lets = Vec::new();
497 let mut locals = Vec::new();
498 let mut names = Vec::from_fn(self.name_positions.len(), |_| None);
499 let mut pats = Vec::new();
500 let mut heads = Vec::new();
502 // First, declare all of our methods that are statics
503 for method in self.method_statics.move_iter() {
504 let decl = respan(self.fmtsp, ast::DeclItem(method));
505 lets.push(P(respan(self.fmtsp,
506 ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))));
509 // Next, build up the static array which will become our precompiled
511 let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
512 let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name);
513 let piece_ty = self.ecx.ty_rptr(
515 self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
516 Some(static_lifetime),
518 lets.push(Context::item_static_array(self.ecx,
523 // Then, build up the static array which will store our precompiled
524 // nonstandard placeholders, if there are any.
525 let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
526 if !self.all_pieces_simple {
527 let piece_ty = self.ecx.ty_path(self.ecx.path_all(
529 true, Context::rtpath(self.ecx, "Argument"),
530 vec![static_lifetime],
533 lets.push(Context::item_static_array(self.ecx,
539 // Right now there is a bug such that for the expression:
541 // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
542 // valid for the call to `foo`. To work around this all arguments to the
543 // format! string are shoved into locals. Furthermore, we shove the address
544 // of each variable because we don't want to move out of the arguments
545 // passed to this function.
546 for (i, e) in self.args.move_iter().enumerate() {
547 let arg_ty = match self.arg_types.get(i).as_ref() {
549 None => continue // error already generated
552 let name = self.ecx.ident_of(format!("__arg{}", i).as_slice());
553 pats.push(self.ecx.pat_ident(e.span, name));
554 locals.push(Context::format_arg(self.ecx, e.span, arg_ty,
555 self.ecx.expr_ident(e.span, name)));
556 heads.push(self.ecx.expr_addr_of(e.span, e));
558 for name in self.name_ordering.iter() {
559 let e = match self.names.pop(name) {
563 let arg_ty = match self.name_types.find(name) {
568 let lname = self.ecx.ident_of(format!("__arg{}",
570 pats.push(self.ecx.pat_ident(e.span, lname));
571 *names.get_mut(*self.name_positions.get(name)) =
572 Some(Context::format_arg(self.ecx, e.span, arg_ty,
573 self.ecx.expr_ident(e.span, lname)));
574 heads.push(self.ecx.expr_addr_of(e.span, e));
577 // Now create a vector containing all the arguments
578 let slicename = self.ecx.ident_of("__args_vec");
580 let args = names.move_iter().map(|a| a.unwrap());
581 let mut args = locals.move_iter().chain(args);
582 let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
583 lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
586 // Now create the fmt::Arguments struct with all our locals we created.
587 let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
588 let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
590 let (fn_name, fn_args) = if self.all_pieces_simple {
591 ("new", vec![pieces, args_slice])
593 let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
594 ("with_placeholders", vec![pieces, fmt, args_slice])
597 let result = self.ecx.expr_call_global(self.fmtsp, vec!(
598 self.ecx.ident_of("std"),
599 self.ecx.ident_of("fmt"),
600 self.ecx.ident_of("Arguments"),
601 self.ecx.ident_of(fn_name)), fn_args);
603 // We did all the work of making sure that the arguments
604 // structure is safe, so we can safely have an unsafe block.
605 let result = self.ecx.expr_block(P(ast::Block {
606 view_items: Vec::new(),
609 id: ast::DUMMY_NODE_ID,
610 rules: ast::UnsafeBlock(ast::CompilerGenerated),
613 let resname = self.ecx.ident_of("__args");
614 lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
615 let res = self.ecx.expr_ident(self.fmtsp, resname);
616 let result = match invocation {
619 self.ecx.expr_call(span, e,
620 vec!(self.ecx.expr_addr_of(span, res)))
622 MethodCall(e, m) => {
624 self.ecx.expr_method_call(span, e, m,
625 vec!(self.ecx.expr_addr_of(span, res)))
628 let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
631 // Constructs an AST equivalent to:
633 // match (&arg0, &arg1) {
634 // (tmp0, tmp1) => body
643 // Because of #11585 the new temporary lifetime rule, the enclosing
644 // statements for these temporaries become the let's themselves.
645 // If one or more of them are RefCell's, RefCell borrow() will also
646 // end there; they don't last long enough for body to use them. The
647 // match expression solves the scope problem.
649 // Note, it may also very well be transformed to:
654 // ref tmp1 => body } } }
656 // But the nested match expression is proved to perform not as well
657 // as series of let's; the first approach does.
658 let pat = self.ecx.pat(self.fmtsp, ast::PatTup(pats));
659 let arm = self.ecx.arm(self.fmtsp, vec!(pat), body);
660 let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
661 self.ecx.expr_match(self.fmtsp, head, vec!(arm))
664 fn format_arg(ecx: &ExtCtxt, sp: Span,
665 ty: &ArgumentType, arg: P<ast::Expr>)
667 let (krate, fmt_fn) = match *ty {
668 Known(ref tyname) => {
669 match tyname.as_slice() {
670 "" => ("std", "secret_show"),
671 "?" => ("debug", "secret_poly"),
672 "b" => ("std", "secret_bool"),
673 "c" => ("std", "secret_char"),
674 "d" | "i" => ("std", "secret_signed"),
675 "e" => ("std", "secret_lower_exp"),
676 "E" => ("std", "secret_upper_exp"),
677 "f" => ("std", "secret_float"),
678 "o" => ("std", "secret_octal"),
679 "p" => ("std", "secret_pointer"),
680 "s" => ("std", "secret_string"),
681 "t" => ("std", "secret_binary"),
682 "u" => ("std", "secret_unsigned"),
683 "x" => ("std", "secret_lower_hex"),
684 "X" => ("std", "secret_upper_hex"),
687 format!("unknown format trait `{}`",
688 *tyname).as_slice());
694 return ecx.expr_call_global(sp, vec![
697 ecx.ident_of("argumentstr")], vec![arg])
700 return ecx.expr_call_global(sp, vec![
703 ecx.ident_of("argumentuint")], vec![arg])
707 let format_fn = ecx.path_global(sp, vec![
710 ecx.ident_of(fmt_fn)]);
711 ecx.expr_call_global(sp, vec![
714 ecx.ident_of("argument")], vec![ecx.expr_path(format_fn), arg])
718 pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
719 tts: &[ast::TokenTree])
720 -> Box<base::MacResult+'cx> {
722 match parse_args(ecx, sp, false, tts) {
723 (invocation, Some((efmt, args, order, names))) => {
724 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
727 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
731 pub fn expand_format_args_method<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
732 tts: &[ast::TokenTree]) -> Box<base::MacResult+'cx> {
734 match parse_args(ecx, sp, true, tts) {
735 (invocation, Some((efmt, args, order, names))) => {
736 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
739 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
743 /// Take the various parts of `format_args!(extra, efmt, args...,
744 /// name=names...)` and construct the appropriate formatting
746 pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
747 invocation: Invocation,
749 args: Vec<P<ast::Expr>>,
750 name_ordering: Vec<String>,
751 names: HashMap<String, P<ast::Expr>>)
753 let arg_types = Vec::from_fn(args.len(), |_| None);
754 let mut cx = Context {
757 arg_types: arg_types,
759 name_positions: HashMap::new(),
760 name_types: HashMap::new(),
761 name_ordering: name_ordering,
764 literal: String::new(),
766 str_pieces: Vec::new(),
767 all_pieces_simple: true,
768 method_statics: Vec::new(),
771 cx.fmtsp = efmt.span;
772 let fmt = match expr_to_string(cx.ecx,
774 "format argument must be a string literal.") {
775 Some((fmt, _)) => fmt,
776 None => return DummyResult::raw_expr(sp)
779 let mut parser = parse::Parser::new(fmt.get());
781 match parser.next() {
783 if parser.errors.len() > 0 { break }
784 cx.verify_piece(&piece);
785 match cx.trans_piece(&piece) {
787 let s = cx.trans_literal_string();
788 cx.str_pieces.push(s);
789 cx.pieces.push(piece);
797 match parser.errors.shift() {
799 cx.ecx.span_err(cx.fmtsp,
800 format!("invalid format string: {}",
802 return DummyResult::raw_expr(sp);
806 if !cx.literal.is_empty() {
807 let s = cx.trans_literal_string();
808 cx.str_pieces.push(s);
811 // Make sure that all arguments were used and all arguments have types.
812 for (i, ty) in cx.arg_types.iter().enumerate() {
814 cx.ecx.span_err(cx.args.get(i).span, "argument never used");
817 for (name, e) in cx.names.iter() {
818 if !cx.name_types.contains_key(name) {
819 cx.ecx.span_err(e.span, "named argument never used");
823 cx.to_expr(invocation)