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;
24 #[deriving(PartialEq)]
26 Known(string::String),
32 Named(string::String),
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::String, P<ast::Expr>>,
48 name_types: HashMap<string::String, ArgumentType>,
49 name_ordering: Vec<string::String>,
51 /// The latest consecutive literal strings, or empty if there weren't any.
52 literal: string::String,
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::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::String>,
85 HashMap<string::String, P<ast::Expr>>)>) {
86 let mut args = Vec::new();
87 let mut names = HashMap::<string::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 || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
121 let ident = match p.token {
122 token::Ident(i, _) => {
128 "expected ident, positional arguments \
129 cannot follow named arguments");
130 return (invocation, None);
134 format!("expected ident for named argument, found `{}`",
135 p.this_token_to_string()).as_slice());
136 return (invocation, None);
139 let interned_name = token::get_ident(ident);
140 let name = interned_name.get();
141 p.expect(&token::Eq);
142 let e = p.parse_expr();
143 match names.find_equiv(&name) {
147 format!("duplicate argument named `{}`",
149 ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
153 order.push(name.to_string());
154 names.insert(name.to_string(), e);
156 args.push(p.parse_expr());
159 return (invocation, Some((fmtstr, args, order, names)));
162 impl<'a, 'b> Context<'a, 'b> {
163 /// Verifies one piece of a parse string. All errors are not emitted as
164 /// fatal so we can continue giving errors about this and possibly other
166 fn verify_piece(&mut self, p: &parse::Piece) {
168 parse::String(..) => {}
169 parse::NextArgument(ref arg) => {
170 // width/precision first, if they have implicit positional
171 // parameters it makes more sense to consume them first.
172 self.verify_count(arg.format.width);
173 self.verify_count(arg.format.precision);
175 // argument second, if it's an implicit positional parameter
176 // it's written second, so it should come after width/precision.
177 let pos = match arg.position {
178 parse::ArgumentNext => {
179 let i = self.next_arg;
180 if self.check_positional_ok() {
185 parse::ArgumentIs(i) => Exact(i),
186 parse::ArgumentNamed(s) => Named(s.to_string()),
189 let ty = Known(arg.format.ty.to_string());
190 self.verify_arg_type(pos, ty);
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_string()), Unsigned);
204 parse::CountIsNextParam => {
205 if self.check_positional_ok() {
206 let next_arg = self.next_arg;
207 self.verify_arg_type(Exact(next_arg), Unsigned);
214 fn check_positional_ok(&mut self) -> bool {
215 if self.nest_level != 0 {
216 self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
217 arguments nested inside methods");
224 fn describe_num_args(&self) -> string::String {
225 match self.args.len() {
226 0 => "no arguments given".to_string(),
227 1 => "there is 1 argument".to_string(),
228 x => format!("there are {} arguments", x),
232 fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
235 if self.args.len() <= arg {
236 let msg = format!("invalid reference to argument `{}` ({:s})",
237 arg, self.describe_num_args());
239 self.ecx.span_err(self.fmtsp, msg.as_slice());
243 let arg_type = match self.arg_types[arg] {
245 Some(ref x) => Some(x)
247 self.verify_same(self.args[arg].span, &ty, arg_type);
249 if self.arg_types[arg].is_none() {
250 *self.arg_types.get_mut(arg) = Some(ty);
255 let span = match self.names.find(&name) {
258 let msg = format!("there is no argument named `{}`", name);
259 self.ecx.span_err(self.fmtsp, msg.as_slice());
263 self.verify_same(span, &ty, self.name_types.find(&name));
264 if !self.name_types.contains_key(&name) {
265 self.name_types.insert(name.clone(), ty);
267 // Assign this named argument a slot in the arguments array if
268 // it hasn't already been assigned a slot.
269 if !self.name_positions.contains_key(&name) {
270 let slot = self.name_positions.len();
271 self.name_positions.insert(name, slot);
277 /// When we're keeping track of the types that are declared for certain
278 /// arguments, we assume that `None` means we haven't seen this argument
279 /// yet, `Some(None)` means that we've seen the argument, but no format was
280 /// specified, and `Some(Some(x))` means that the argument was declared to
283 /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
284 /// that: `Some(None) == Some(Some(x))`
285 fn verify_same(&self,
288 before: Option<&ArgumentType>) {
289 let cur = match before {
297 (&Known(ref cur), &Known(ref ty)) => {
298 self.ecx.span_err(sp,
299 format!("argument redeclared with type `{}` when \
300 it was previously `{}`",
304 (&Known(ref cur), _) => {
305 self.ecx.span_err(sp,
306 format!("argument used to format with `{}` was \
307 attempted to not be used for formatting",
310 (_, &Known(ref ty)) => {
311 self.ecx.span_err(sp,
312 format!("argument previously used as a format \
313 argument attempted to be used as `{}`",
317 self.ecx.span_err(sp, "argument declared with multiple formats");
322 /// These attributes are applied to all statics that this syntax extension
324 fn static_attrs(ecx: &ExtCtxt, fmtsp: Span) -> Vec<ast::Attribute> {
325 // Flag statics as `inline` so LLVM can merge duplicate globals as much
326 // as possible (which we're generating a whole lot of).
327 let unnamed = ecx.meta_word(fmtsp, InternedString::new("inline"));
328 let unnamed = ecx.attribute(fmtsp, unnamed);
330 // Do not warn format string as dead code
331 let dead_code = ecx.meta_word(fmtsp, InternedString::new("dead_code"));
332 let allow_dead_code = ecx.meta_list(fmtsp,
333 InternedString::new("allow"),
335 let allow_dead_code = ecx.attribute(fmtsp, allow_dead_code);
336 vec![unnamed, allow_dead_code]
339 fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
340 vec![ecx.ident_of("std"), ecx.ident_of("fmt"), ecx.ident_of("rt"), ecx.ident_of(s)]
343 fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
346 parse::CountIs(i) => {
347 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIs"),
348 vec!(self.ecx.expr_uint(sp, i)))
350 parse::CountIsParam(i) => {
351 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIsParam"),
352 vec!(self.ecx.expr_uint(sp, i)))
354 parse::CountImplied => {
355 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
357 self.ecx.expr_path(path)
359 parse::CountIsNextParam => {
360 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
361 "CountIsNextParam"));
362 self.ecx.expr_path(path)
364 parse::CountIsName(n) => {
365 let i = match self.name_positions.find_equiv(&n) {
367 None => 0, // error already emitted elsewhere
369 let i = i + self.args.len();
370 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "CountIsParam"),
371 vec!(self.ecx.expr_uint(sp, i)))
376 /// Translate the accumulated string literals to a literal expression
377 fn trans_literal_string(&mut self) -> P<ast::Expr> {
379 let s = token::intern_and_get_ident(self.literal.as_slice());
380 self.literal.clear();
381 self.ecx.expr_str(sp, s)
384 /// Translate a `parse::Piece` to a static `rt::Argument` or append
385 /// to the `literal` string.
386 fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> {
389 parse::String(s) => {
390 self.literal.push_str(s);
393 parse::NextArgument(ref arg) => {
394 // Translate the position
395 let pos = match arg.position {
396 // These two have a direct mapping
397 parse::ArgumentNext => {
398 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx,
400 self.ecx.expr_path(path)
402 parse::ArgumentIs(i) => {
403 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "ArgumentIs"),
404 vec!(self.ecx.expr_uint(sp, i)))
406 // Named arguments are converted to positional arguments at
407 // the end of the list of arguments
408 parse::ArgumentNamed(n) => {
409 let i = match self.name_positions.find_equiv(&n) {
411 None => 0, // error already emitted elsewhere
413 let i = i + self.args.len();
414 self.ecx.expr_call_global(sp, Context::rtpath(self.ecx, "ArgumentIs"),
415 vec!(self.ecx.expr_uint(sp, i)))
419 let simple_arg = parse::Argument {
420 position: parse::ArgumentNext,
421 format: parse::FormatSpec {
422 fill: arg.format.fill,
423 align: parse::AlignUnknown,
425 precision: parse::CountImplied,
426 width: parse::CountImplied,
431 let fill = match arg.format.fill { Some(c) => c, None => ' ' };
433 if *arg != simple_arg || fill != ' ' {
434 self.all_pieces_simple = false;
437 // Translate the format
438 let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
439 let align = match arg.format.align {
440 parse::AlignLeft => {
441 self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignLeft"))
443 parse::AlignRight => {
444 self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignRight"))
446 parse::AlignCenter => {
447 self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignCenter"))
449 parse::AlignUnknown => {
450 self.ecx.path_global(sp, Context::rtpath(self.ecx, "AlignUnknown"))
453 let align = self.ecx.expr_path(align);
454 let flags = self.ecx.expr_uint(sp, arg.format.flags);
455 let prec = self.trans_count(arg.format.precision);
456 let width = self.trans_count(arg.format.width);
457 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
458 let fmt = self.ecx.expr_struct(sp, path, vec!(
459 self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
460 self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
461 self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
462 self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
463 self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
465 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument"));
466 Some(self.ecx.expr_struct(sp, path, vec!(
467 self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
468 self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))))
473 fn item_static_array(ecx: &mut ExtCtxt,
475 piece_ty: P<ast::Ty>,
476 pieces: Vec<P<ast::Expr>>)
478 let fmtsp = piece_ty.span;
479 let pieces_len = ecx.expr_uint(fmtsp, pieces.len());
480 let fmt = ecx.expr_vec(fmtsp, pieces);
481 let ty = ast::TyFixedLengthVec(
485 let ty = ecx.ty(fmtsp, ty);
486 let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
487 let item = ecx.item(fmtsp, name, Context::static_attrs(ecx, fmtsp), st);
488 let decl = respan(fmtsp, ast::DeclItem(item));
489 P(respan(fmtsp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
492 /// Actually builds the expression which the iformat! block will be expanded
494 fn to_expr(mut self, invocation: Invocation) -> P<ast::Expr> {
495 let mut lets = Vec::new();
496 let mut locals = Vec::new();
497 let mut names = Vec::from_fn(self.name_positions.len(), |_| None);
498 let mut pats = Vec::new();
499 let mut heads = Vec::new();
501 // First, declare all of our methods that are statics
502 for method in self.method_statics.into_iter() {
503 let decl = respan(self.fmtsp, ast::DeclItem(method));
504 lets.push(P(respan(self.fmtsp,
505 ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))));
508 // Next, build up the static array which will become our precompiled
510 let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
511 let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name);
512 let piece_ty = self.ecx.ty_rptr(
514 self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
515 Some(static_lifetime),
517 lets.push(Context::item_static_array(self.ecx,
522 // Then, build up the static array which will store our precompiled
523 // nonstandard placeholders, if there are any.
524 let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
525 if !self.all_pieces_simple {
526 let piece_ty = self.ecx.ty_path(self.ecx.path_all(
528 true, Context::rtpath(self.ecx, "Argument"),
529 vec![static_lifetime],
532 lets.push(Context::item_static_array(self.ecx,
538 // Right now there is a bug such that for the expression:
540 // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
541 // valid for the call to `foo`. To work around this all arguments to the
542 // format! string are shoved into locals. Furthermore, we shove the address
543 // of each variable because we don't want to move out of the arguments
544 // passed to this function.
545 for (i, e) in self.args.into_iter().enumerate() {
546 let arg_ty = match self.arg_types[i].as_ref() {
548 None => continue // error already generated
551 let name = self.ecx.ident_of(format!("__arg{}", i).as_slice());
552 pats.push(self.ecx.pat_ident(e.span, name));
553 locals.push(Context::format_arg(self.ecx, e.span, arg_ty,
554 self.ecx.expr_ident(e.span, name)));
555 heads.push(self.ecx.expr_addr_of(e.span, e));
557 for name in self.name_ordering.iter() {
558 let e = match self.names.pop(name) {
562 let arg_ty = match self.name_types.find(name) {
567 let lname = self.ecx.ident_of(format!("__arg{}",
569 pats.push(self.ecx.pat_ident(e.span, lname));
570 *names.get_mut(self.name_positions[*name]) =
571 Some(Context::format_arg(self.ecx, e.span, arg_ty,
572 self.ecx.expr_ident(e.span, lname)));
573 heads.push(self.ecx.expr_addr_of(e.span, e));
576 // Now create a vector containing all the arguments
577 let slicename = self.ecx.ident_of("__args_vec");
579 let args = names.into_iter().map(|a| a.unwrap());
580 let mut args = locals.into_iter().chain(args);
581 let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
582 lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
585 // Now create the fmt::Arguments struct with all our locals we created.
586 let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
587 let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
589 let (fn_name, fn_args) = if self.all_pieces_simple {
590 ("new", vec![pieces, args_slice])
592 let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
593 ("with_placeholders", vec![pieces, fmt, args_slice])
596 let result = self.ecx.expr_call_global(self.fmtsp, vec!(
597 self.ecx.ident_of("std"),
598 self.ecx.ident_of("fmt"),
599 self.ecx.ident_of("Arguments"),
600 self.ecx.ident_of(fn_name)), fn_args);
602 // We did all the work of making sure that the arguments
603 // structure is safe, so we can safely have an unsafe block.
604 let result = self.ecx.expr_block(P(ast::Block {
605 view_items: Vec::new(),
608 id: ast::DUMMY_NODE_ID,
609 rules: ast::UnsafeBlock(ast::CompilerGenerated),
612 let resname = self.ecx.ident_of("__args");
613 lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
614 let res = self.ecx.expr_ident(self.fmtsp, resname);
615 let result = match invocation {
618 self.ecx.expr_call(span, e,
619 vec!(self.ecx.expr_addr_of(span, res)))
621 MethodCall(e, m) => {
623 self.ecx.expr_method_call(span, e, m,
624 vec!(self.ecx.expr_addr_of(span, res)))
627 let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
630 // Constructs an AST equivalent to:
632 // match (&arg0, &arg1) {
633 // (tmp0, tmp1) => body
642 // Because of #11585 the new temporary lifetime rule, the enclosing
643 // statements for these temporaries become the let's themselves.
644 // If one or more of them are RefCell's, RefCell borrow() will also
645 // end there; they don't last long enough for body to use them. The
646 // match expression solves the scope problem.
648 // Note, it may also very well be transformed to:
653 // ref tmp1 => body } } }
655 // But the nested match expression is proved to perform not as well
656 // as series of let's; the first approach does.
657 let pat = self.ecx.pat(self.fmtsp, ast::PatTup(pats));
658 let arm = self.ecx.arm(self.fmtsp, vec!(pat), body);
659 let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
660 self.ecx.expr_match(self.fmtsp, head, vec!(arm))
663 fn format_arg(ecx: &ExtCtxt, sp: Span,
664 ty: &ArgumentType, arg: P<ast::Expr>)
666 let (krate, fmt_fn) = match *ty {
667 Known(ref tyname) => {
668 match tyname.as_slice() {
669 "" => ("std", "secret_show"),
670 "b" => ("std", "secret_bool"),
671 "c" => ("std", "secret_char"),
672 "d" | "i" => ("std", "secret_signed"),
673 "e" => ("std", "secret_lower_exp"),
674 "E" => ("std", "secret_upper_exp"),
675 "f" => ("std", "secret_float"),
676 "o" => ("std", "secret_octal"),
677 "p" => ("std", "secret_pointer"),
678 "s" => ("std", "secret_string"),
679 "t" => ("std", "secret_binary"),
680 "u" => ("std", "secret_unsigned"),
681 "x" => ("std", "secret_lower_hex"),
682 "X" => ("std", "secret_upper_hex"),
685 format!("unknown format trait `{}`",
686 *tyname).as_slice());
692 return ecx.expr_call_global(sp, vec![
695 ecx.ident_of("argumentuint")], vec![arg])
699 let format_fn = ecx.path_global(sp, vec![
702 ecx.ident_of(fmt_fn)]);
703 ecx.expr_call_global(sp, vec![
706 ecx.ident_of("argument")], vec![ecx.expr_path(format_fn), arg])
710 pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
711 tts: &[ast::TokenTree])
712 -> Box<base::MacResult+'cx> {
714 match parse_args(ecx, sp, false, tts) {
715 (invocation, Some((efmt, args, order, names))) => {
716 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
719 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
723 pub fn expand_format_args_method<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
724 tts: &[ast::TokenTree]) -> Box<base::MacResult+'cx> {
726 match parse_args(ecx, sp, true, tts) {
727 (invocation, Some((efmt, args, order, names))) => {
728 MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
731 (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
735 /// Take the various parts of `format_args!(extra, efmt, args...,
736 /// name=names...)` and construct the appropriate formatting
738 pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
739 invocation: Invocation,
741 args: Vec<P<ast::Expr>>,
742 name_ordering: Vec<string::String>,
743 names: HashMap<string::String, P<ast::Expr>>)
745 let arg_types = Vec::from_fn(args.len(), |_| None);
746 let mut cx = Context {
749 arg_types: arg_types,
751 name_positions: HashMap::new(),
752 name_types: HashMap::new(),
753 name_ordering: name_ordering,
756 literal: string::String::new(),
758 str_pieces: Vec::new(),
759 all_pieces_simple: true,
760 method_statics: Vec::new(),
763 cx.fmtsp = efmt.span;
764 let fmt = match expr_to_string(cx.ecx,
766 "format argument must be a string literal.") {
767 Some((fmt, _)) => fmt,
768 None => return DummyResult::raw_expr(sp)
771 let mut parser = parse::Parser::new(fmt.get());
773 match parser.next() {
775 if parser.errors.len() > 0 { break }
776 cx.verify_piece(&piece);
777 match cx.trans_piece(&piece) {
779 let s = cx.trans_literal_string();
780 cx.str_pieces.push(s);
781 cx.pieces.push(piece);
789 match parser.errors.remove(0) {
791 cx.ecx.span_err(cx.fmtsp,
792 format!("invalid format string: {}",
794 return DummyResult::raw_expr(sp);
798 if !cx.literal.is_empty() {
799 let s = cx.trans_literal_string();
800 cx.str_pieces.push(s);
803 // Make sure that all arguments were used and all arguments have types.
804 for (i, ty) in cx.arg_types.iter().enumerate() {
806 cx.ecx.span_err(cx.args[i].span, "argument never used");
809 for (name, e) in cx.names.iter() {
810 if !cx.name_types.contains_key(name) {
811 cx.ecx.span_err(e.span, "named argument never used");
815 cx.to_expr(invocation)