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.
11 use self::ArgumentType
::*;
12 use self::Position
::*;
14 use fmt_macros
as parse
;
17 use syntax
::codemap
::{Span, respan}
;
18 use syntax
::ext
::base
::*;
19 use syntax
::ext
::base
;
20 use syntax
::ext
::build
::AstBuilder
;
21 use syntax
::fold
::Folder
;
22 use syntax
::parse
::token
::special_idents
;
23 use syntax
::parse
::token
;
26 use std
::collections
::HashMap
;
39 struct Context
<'a
, 'b
:'a
> {
40 ecx
: &'a
mut ExtCtxt
<'b
>,
41 /// The macro's call site. References to unstable formatting internals must
42 /// use this span to pass the stability checker.
44 /// The span of the format string literal.
47 /// Parsed argument expressions and the types that we've found so far for
49 args
: Vec
<P
<ast
::Expr
>>,
50 arg_types
: Vec
<Option
<ArgumentType
>>,
51 /// Parsed named expressions and the types that we've found for them so far.
52 /// Note that we keep a side-array of the ordering of the named arguments
53 /// found to be sure that we can translate them in the same order that they
55 names
: HashMap
<String
, P
<ast
::Expr
>>,
56 name_types
: HashMap
<String
, ArgumentType
>,
57 name_ordering
: Vec
<String
>,
59 /// The latest consecutive literal strings, or empty if there weren't any.
62 /// Collection of the compiled `rt::Argument` structures
63 pieces
: Vec
<P
<ast
::Expr
>>,
64 /// Collection of string literals
65 str_pieces
: Vec
<P
<ast
::Expr
>>,
66 /// Stays `true` if all formatting parameters are default (as in "{}{}").
67 all_pieces_simple
: bool
,
69 name_positions
: HashMap
<String
, usize>,
71 /// Updated as arguments are consumed or methods are entered
76 /// Parses the arguments from the given list of tokens, returning None
77 /// if there's a parse error so we can continue parsing other format!
80 /// If parsing succeeds, the return value is:
82 /// Some((fmtstr, unnamed arguments, ordering of named arguments,
85 fn parse_args(ecx
: &mut ExtCtxt
, sp
: Span
, tts
: &[ast
::TokenTree
])
86 -> Option
<(P
<ast
::Expr
>, Vec
<P
<ast
::Expr
>>, Vec
<String
>,
87 HashMap
<String
, P
<ast
::Expr
>>)> {
88 let mut args
= Vec
::new();
89 let mut names
= HashMap
::<String
, P
<ast
::Expr
>>::new();
90 let mut order
= Vec
::new();
92 let mut p
= ecx
.new_parser_from_tts(tts
);
94 if p
.token
== token
::Eof
{
95 ecx
.span_err(sp
, "requires at least a format string argument");
98 let fmtstr
= panictry
!(p
.parse_expr());
99 let mut named
= false;
100 while p
.token
!= token
::Eof
{
101 if !p
.eat(&token
::Comma
) {
102 ecx
.span_err(sp
, "expected token: `,`");
105 if p
.token
== token
::Eof { break }
// accept trailing commas
106 if named
|| (p
.token
.is_ident() && p
.look_ahead(1, |t
| *t
== token
::Eq
)) {
108 let ident
= match p
.token
{
109 token
::Ident(i
, _
) => {
115 "expected ident, positional arguments \
116 cannot follow named arguments");
121 &format
!("expected ident for named argument, found `{}`",
122 p
.this_token_to_string()));
126 let name
: &str = &ident
.name
.as_str();
128 panictry
!(p
.expect(&token
::Eq
));
129 let e
= panictry
!(p
.parse_expr());
130 match names
.get(name
) {
133 ecx
.struct_span_err(e
.span
,
134 &format
!("duplicate argument named `{}`",
136 .span_note(prev
.span
, "previously here")
141 order
.push(name
.to_string());
142 names
.insert(name
.to_string(), e
);
144 args
.push(panictry
!(p
.parse_expr()));
147 Some((fmtstr
, args
, order
, names
))
150 impl<'a
, 'b
> Context
<'a
, 'b
> {
151 /// Verifies one piece of a parse string. All errors are not emitted as
152 /// fatal so we can continue giving errors about this and possibly other
154 fn verify_piece(&mut self, p
: &parse
::Piece
) {
156 parse
::String(..) => {}
157 parse
::NextArgument(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_string()),
177 let ty
= Known(arg
.format
.ty
.to_string());
178 self.verify_arg_type(pos
, ty
);
183 fn verify_count(&mut self, c
: parse
::Count
) {
185 parse
::CountImplied
| parse
::CountIs(..) => {}
186 parse
::CountIsParam(i
) => {
187 self.verify_arg_type(Exact(i
), Unsigned
);
189 parse
::CountIsName(s
) => {
190 self.verify_arg_type(Named(s
.to_string()), Unsigned
);
192 parse
::CountIsNextParam
=> {
193 if self.check_positional_ok() {
194 let next_arg
= self.next_arg
;
195 self.verify_arg_type(Exact(next_arg
), Unsigned
);
202 fn check_positional_ok(&mut self) -> bool
{
203 if self.nest_level
!= 0 {
204 self.ecx
.span_err(self.fmtsp
, "cannot use implicit positional \
205 arguments nested inside methods");
212 fn describe_num_args(&self) -> String
{
213 match self.args
.len() {
214 0 => "no arguments given".to_string(),
215 1 => "there is 1 argument".to_string(),
216 x
=> format
!("there are {} arguments", x
),
220 fn verify_arg_type(&mut self, arg
: Position
, ty
: ArgumentType
) {
223 if self.args
.len() <= arg
{
224 let msg
= format
!("invalid reference to argument `{}` ({})",
225 arg
, self.describe_num_args());
227 self.ecx
.span_err(self.fmtsp
, &msg
[..]);
231 let arg_type
= match self.arg_types
[arg
] {
233 Some(ref x
) => Some(x
)
235 self.verify_same(self.args
[arg
].span
, &ty
, arg_type
);
237 if self.arg_types
[arg
].is_none() {
238 self.arg_types
[arg
] = Some(ty
);
243 let span
= match self.names
.get(&name
) {
246 let msg
= format
!("there is no argument named `{}`", name
);
247 self.ecx
.span_err(self.fmtsp
, &msg
[..]);
251 self.verify_same(span
, &ty
, self.name_types
.get(&name
));
252 if !self.name_types
.contains_key(&name
) {
253 self.name_types
.insert(name
.clone(), ty
);
255 // Assign this named argument a slot in the arguments array if
256 // it hasn't already been assigned a slot.
257 if !self.name_positions
.contains_key(&name
) {
258 let slot
= self.name_positions
.len();
259 self.name_positions
.insert(name
, slot
);
265 /// When we're keeping track of the types that are declared for certain
266 /// arguments, we assume that `None` means we haven't seen this argument
267 /// yet, `Some(None)` means that we've seen the argument, but no format was
268 /// specified, and `Some(Some(x))` means that the argument was declared to
271 /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
272 /// that: `Some(None) == Some(Some(x))`
273 fn verify_same(&self,
276 before
: Option
<&ArgumentType
>) {
277 let cur
= match before
{
285 (&Known(ref cur
), &Known(ref ty
)) => {
286 self.ecx
.span_err(sp
,
287 &format
!("argument redeclared with type `{}` when \
288 it was previously `{}`",
292 (&Known(ref cur
), _
) => {
293 self.ecx
.span_err(sp
,
294 &format
!("argument used to format with `{}` was \
295 attempted to not be used for formatting",
298 (_
, &Known(ref ty
)) => {
299 self.ecx
.span_err(sp
,
300 &format
!("argument previously used as a format \
301 argument attempted to be used as `{}`",
305 self.ecx
.span_err(sp
, "argument declared with multiple formats");
310 fn rtpath(ecx
: &ExtCtxt
, s
: &str) -> Vec
<ast
::Ident
> {
311 ecx
.std_path(&["fmt", "rt", "v1", s
])
314 fn trans_count(&self, c
: parse
::Count
) -> P
<ast
::Expr
> {
316 let count
= |c
, arg
| {
317 let mut path
= Context
::rtpath(self.ecx
, "Count");
318 path
.push(self.ecx
.ident_of(c
));
320 Some(arg
) => self.ecx
.expr_call_global(sp
, path
, vec
![arg
]),
321 None
=> self.ecx
.expr_path(self.ecx
.path_global(sp
, path
)),
325 parse
::CountIs(i
) => count("Is", Some(self.ecx
.expr_usize(sp
, i
))),
326 parse
::CountIsParam(i
) => {
327 count("Param", Some(self.ecx
.expr_usize(sp
, i
)))
329 parse
::CountImplied
=> count("Implied", None
),
330 parse
::CountIsNextParam
=> count("NextParam", None
),
331 parse
::CountIsName(n
) => {
332 let i
= match self.name_positions
.get(n
) {
334 None
=> 0, // error already emitted elsewhere
336 let i
= i
+ self.args
.len();
337 count("Param", Some(self.ecx
.expr_usize(sp
, i
)))
342 /// Translate the accumulated string literals to a literal expression
343 fn trans_literal_string(&mut self) -> P
<ast
::Expr
> {
345 let s
= token
::intern_and_get_ident(&self.literal
);
346 self.literal
.clear();
347 self.ecx
.expr_str(sp
, s
)
350 /// Translate a `parse::Piece` to a static `rt::Argument` or append
351 /// to the `literal` string.
352 fn trans_piece(&mut self, piece
: &parse
::Piece
) -> Option
<P
<ast
::Expr
>> {
355 parse
::String(s
) => {
356 self.literal
.push_str(s
);
359 parse
::NextArgument(ref arg
) => {
360 // Translate the position
363 let mut path
= Context
::rtpath(self.ecx
, "Position");
364 path
.push(self.ecx
.ident_of(c
));
367 let arg
= self.ecx
.expr_usize(sp
, i
);
368 self.ecx
.expr_call_global(sp
, path
, vec
![arg
])
371 self.ecx
.expr_path(self.ecx
.path_global(sp
, path
))
376 // These two have a direct mapping
377 parse
::ArgumentNext
=> pos("Next", None
),
378 parse
::ArgumentIs(i
) => pos("At", Some(i
)),
380 // Named arguments are converted to positional arguments
381 // at the end of the list of arguments
382 parse
::ArgumentNamed(n
) => {
383 let i
= match self.name_positions
.get(n
) {
385 None
=> 0, // error already emitted elsewhere
387 let i
= i
+ self.args
.len();
393 let simple_arg
= parse
::Argument
{
394 position
: parse
::ArgumentNext
,
395 format
: parse
::FormatSpec
{
396 fill
: arg
.format
.fill
,
397 align
: parse
::AlignUnknown
,
399 precision
: parse
::CountImplied
,
400 width
: parse
::CountImplied
,
405 let fill
= match arg
.format
.fill { Some(c) => c, None => ' ' }
;
407 if *arg
!= simple_arg
|| fill
!= ' '
{
408 self.all_pieces_simple
= false;
411 // Translate the format
412 let fill
= self.ecx
.expr_lit(sp
, ast
::LitChar(fill
));
414 let mut p
= Context
::rtpath(self.ecx
, "Alignment");
415 p
.push(self.ecx
.ident_of(name
));
416 self.ecx
.path_global(sp
, p
)
418 let align
= match arg
.format
.align
{
419 parse
::AlignLeft
=> align("Left"),
420 parse
::AlignRight
=> align("Right"),
421 parse
::AlignCenter
=> align("Center"),
422 parse
::AlignUnknown
=> align("Unknown"),
424 let align
= self.ecx
.expr_path(align
);
425 let flags
= self.ecx
.expr_u32(sp
, arg
.format
.flags
);
426 let prec
= self.trans_count(arg
.format
.precision
);
427 let width
= self.trans_count(arg
.format
.width
);
428 let path
= self.ecx
.path_global(sp
, Context
::rtpath(self.ecx
, "FormatSpec"));
429 let fmt
= self.ecx
.expr_struct(sp
, path
, vec
!(
430 self.ecx
.field_imm(sp
, self.ecx
.ident_of("fill"), fill
),
431 self.ecx
.field_imm(sp
, self.ecx
.ident_of("align"), align
),
432 self.ecx
.field_imm(sp
, self.ecx
.ident_of("flags"), flags
),
433 self.ecx
.field_imm(sp
, self.ecx
.ident_of("precision"), prec
),
434 self.ecx
.field_imm(sp
, self.ecx
.ident_of("width"), width
)));
436 let path
= self.ecx
.path_global(sp
, Context
::rtpath(self.ecx
, "Argument"));
437 Some(self.ecx
.expr_struct(sp
, path
, vec
!(
438 self.ecx
.field_imm(sp
, self.ecx
.ident_of("position"), pos
),
439 self.ecx
.field_imm(sp
, self.ecx
.ident_of("format"), fmt
))))
444 fn static_array(ecx
: &mut ExtCtxt
,
446 piece_ty
: P
<ast
::Ty
>,
447 pieces
: Vec
<P
<ast
::Expr
>>)
449 let sp
= piece_ty
.span
;
450 let ty
= ecx
.ty_rptr(sp
,
451 ecx
.ty(sp
, ast
::TyVec(piece_ty
)),
452 Some(ecx
.lifetime(sp
, special_idents
::static_lifetime
.name
)),
454 let slice
= ecx
.expr_vec_slice(sp
, pieces
);
455 // static instead of const to speed up codegen by not requiring this to be inlined
456 let st
= ast
::ItemStatic(ty
, ast
::MutImmutable
, slice
);
458 let name
= ecx
.ident_of(name
);
459 let item
= ecx
.item(sp
, name
, vec
![], st
);
460 let decl
= respan(sp
, ast
::DeclItem(item
));
462 // Wrap the declaration in a block so that it forms a single expression.
463 ecx
.expr_block(ecx
.block(sp
,
464 vec
![P(respan(sp
, ast
::StmtDecl(P(decl
), ast
::DUMMY_NODE_ID
)))],
465 Some(ecx
.expr_ident(sp
, name
))))
468 /// Actually builds the expression which the iformat! block will be expanded
470 fn into_expr(mut self) -> P
<ast
::Expr
> {
471 let mut locals
= Vec
::new();
472 let mut names
= vec
![None
; self.name_positions
.len()];
473 let mut pats
= Vec
::new();
474 let mut heads
= Vec
::new();
476 // First, build up the static array which will become our precompiled
478 let static_lifetime
= self.ecx
.lifetime(self.fmtsp
, special_idents
::static_lifetime
.name
);
479 let piece_ty
= self.ecx
.ty_rptr(
481 self.ecx
.ty_ident(self.fmtsp
, self.ecx
.ident_of("str")),
482 Some(static_lifetime
),
484 let pieces
= Context
::static_array(self.ecx
,
490 // Right now there is a bug such that for the expression:
492 // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
493 // valid for the call to `foo`. To work around this all arguments to the
494 // format! string are shoved into locals. Furthermore, we shove the address
495 // of each variable because we don't want to move out of the arguments
496 // passed to this function.
497 for (i
, e
) in self.args
.into_iter().enumerate() {
498 let arg_ty
= match self.arg_types
[i
].as_ref() {
500 None
=> continue // error already generated
503 let name
= self.ecx
.ident_of(&format
!("__arg{}", i
));
504 pats
.push(self.ecx
.pat_ident(e
.span
, name
));
505 locals
.push(Context
::format_arg(self.ecx
, self.macsp
, e
.span
, arg_ty
,
506 self.ecx
.expr_ident(e
.span
, name
)));
507 heads
.push(self.ecx
.expr_addr_of(e
.span
, e
));
509 for name
in &self.name_ordering
{
510 let e
= match self.names
.remove(name
) {
514 let arg_ty
= match self.name_types
.get(name
) {
519 let lname
= self.ecx
.ident_of(&format
!("__arg{}",
521 pats
.push(self.ecx
.pat_ident(e
.span
, lname
));
522 names
[*self.name_positions
.get(name
).unwrap()] =
523 Some(Context
::format_arg(self.ecx
, self.macsp
, e
.span
, arg_ty
,
524 self.ecx
.expr_ident(e
.span
, lname
)));
525 heads
.push(self.ecx
.expr_addr_of(e
.span
, e
));
528 // Now create a vector containing all the arguments
529 let args
= locals
.into_iter().chain(names
.into_iter().map(|a
| a
.unwrap()));
531 let args_array
= self.ecx
.expr_vec(self.fmtsp
, args
.collect());
533 // Constructs an AST equivalent to:
535 // match (&arg0, &arg1) {
536 // (tmp0, tmp1) => args_array
545 // Because of #11585 the new temporary lifetime rule, the enclosing
546 // statements for these temporaries become the let's themselves.
547 // If one or more of them are RefCell's, RefCell borrow() will also
548 // end there; they don't last long enough for args_array to use them.
549 // The match expression solves the scope problem.
551 // Note, it may also very well be transformed to:
556 // ref tmp1 => args_array } } }
558 // But the nested match expression is proved to perform not as well
559 // as series of let's; the first approach does.
560 let pat
= self.ecx
.pat_tuple(self.fmtsp
, pats
);
561 let arm
= self.ecx
.arm(self.fmtsp
, vec
!(pat
), args_array
);
562 let head
= self.ecx
.expr(self.fmtsp
, ast
::ExprTup(heads
));
563 let result
= self.ecx
.expr_match(self.fmtsp
, head
, vec
!(arm
));
565 let args_slice
= self.ecx
.expr_addr_of(self.fmtsp
, result
);
567 // Now create the fmt::Arguments struct with all our locals we created.
568 let (fn_name
, fn_args
) = if self.all_pieces_simple
{
569 ("new_v1", vec
![pieces
, args_slice
])
571 // Build up the static array which will store our precompiled
572 // nonstandard placeholders, if there are any.
573 let piece_ty
= self.ecx
.ty_path(self.ecx
.path_global(
575 Context
::rtpath(self.ecx
, "Argument")));
576 let fmt
= Context
::static_array(self.ecx
,
581 ("new_v1_formatted", vec
![pieces
, args_slice
, fmt
])
584 let path
= self.ecx
.std_path(&["fmt", "Arguments", fn_name
]);
585 self.ecx
.expr_call_global(self.macsp
, path
, fn_args
)
588 fn format_arg(ecx
: &ExtCtxt
, macsp
: Span
, sp
: Span
,
589 ty
: &ArgumentType
, arg
: P
<ast
::Expr
>)
591 let trait_
= match *ty
{
592 Known(ref tyname
) => {
605 &format
!("unknown format trait `{}`",
612 let path
= ecx
.std_path(&["fmt", "ArgumentV1", "from_usize"]);
613 return ecx
.expr_call_global(macsp
, path
, vec
![arg
])
617 let path
= ecx
.std_path(&["fmt", trait_
, "fmt"]);
618 let format_fn
= ecx
.path_global(sp
, path
);
619 let path
= ecx
.std_path(&["fmt", "ArgumentV1", "new"]);
620 ecx
.expr_call_global(macsp
, path
, vec
![arg
, ecx
.expr_path(format_fn
)])
624 pub fn expand_format_args
<'cx
>(ecx
: &'cx
mut ExtCtxt
, sp
: Span
,
625 tts
: &[ast
::TokenTree
])
626 -> Box
<base
::MacResult
+'cx
> {
628 match parse_args(ecx
, sp
, tts
) {
629 Some((efmt
, args
, order
, names
)) => {
630 MacEager
::expr(expand_preparsed_format_args(ecx
, sp
, efmt
,
633 None
=> DummyResult
::expr(sp
)
637 /// Take the various parts of `format_args!(efmt, args..., name=names...)`
638 /// and construct the appropriate formatting expression.
639 pub fn expand_preparsed_format_args(ecx
: &mut ExtCtxt
, sp
: Span
,
641 args
: Vec
<P
<ast
::Expr
>>,
642 name_ordering
: Vec
<String
>,
643 names
: HashMap
<String
, P
<ast
::Expr
>>)
645 let arg_types
: Vec
<_
> = (0..args
.len()).map(|_
| None
).collect();
646 let macsp
= ecx
.call_site();
647 // Expand the format literal so that efmt.span will have a backtrace. This
648 // is essential for locating a bug when the format literal is generated in
649 // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
650 let efmt
= ecx
.expander().fold_expr(efmt
);
651 let mut cx
= Context
{
654 arg_types
: arg_types
,
656 name_positions
: HashMap
::new(),
657 name_types
: HashMap
::new(),
658 name_ordering
: name_ordering
,
661 literal
: String
::new(),
663 str_pieces
: Vec
::new(),
664 all_pieces_simple
: true,
668 let fmt
= match expr_to_string(cx
.ecx
,
670 "format argument must be a string literal.") {
671 Some((fmt
, _
)) => fmt
,
672 None
=> return DummyResult
::raw_expr(sp
)
675 let mut parser
= parse
::Parser
::new(&fmt
);
678 match parser
.next() {
680 if !parser
.errors
.is_empty() { break }
681 cx
.verify_piece(&piece
);
682 match cx
.trans_piece(&piece
) {
684 let s
= cx
.trans_literal_string();
685 cx
.str_pieces
.push(s
);
686 cx
.pieces
.push(piece
);
694 if !parser
.errors
.is_empty() {
695 cx
.ecx
.span_err(cx
.fmtsp
, &format
!("invalid format string: {}",
696 parser
.errors
.remove(0)));
697 return DummyResult
::raw_expr(sp
);
699 if !cx
.literal
.is_empty() {
700 let s
= cx
.trans_literal_string();
701 cx
.str_pieces
.push(s
);
704 // Make sure that all arguments were used and all arguments have types.
705 for (i
, ty
) in cx
.arg_types
.iter().enumerate() {
707 cx
.ecx
.span_err(cx
.args
[i
].span
, "argument never used");
710 for (name
, e
) in &cx
.names
{
711 if !cx
.name_types
.contains_key(name
) {
712 cx
.ecx
.span_err(e
.span
, "named argument never used");