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
::*;
15 use codemap
::{Span, respan}
;
18 use ext
::build
::AstBuilder
;
19 use fmt_macros
as parse
;
21 use parse
::token
::special_idents
;
25 use std
::collections
::HashMap
;
26 use std
::iter
::repeat
;
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,
84 fn parse_args(ecx
: &mut ExtCtxt
, sp
: Span
, tts
: &[ast
::TokenTree
])
85 -> Option
<(P
<ast
::Expr
>, Vec
<P
<ast
::Expr
>>, Vec
<String
>,
86 HashMap
<String
, P
<ast
::Expr
>>)> {
87 let mut args
= Vec
::new();
88 let mut names
= HashMap
::<String
, P
<ast
::Expr
>>::new();
89 let mut order
= Vec
::new();
91 let mut p
= ecx
.new_parser_from_tts(tts
);
93 if p
.token
== token
::Eof
{
94 ecx
.span_err(sp
, "requires at least a format string argument");
97 let fmtstr
= p
.parse_expr();
98 let mut named
= false;
99 while p
.token
!= token
::Eof
{
100 if !panictry
!(p
.eat(&token
::Comma
)) {
101 ecx
.span_err(sp
, "expected token: `,`");
104 if p
.token
== token
::Eof { break }
// accept trailing commas
105 if named
|| (p
.token
.is_ident() && p
.look_ahead(1, |t
| *t
== token
::Eq
)) {
107 let ident
= match p
.token
{
108 token
::Ident(i
, _
) => {
114 "expected ident, positional arguments \
115 cannot follow named arguments");
120 &format
!("expected ident for named argument, found `{}`",
121 p
.this_token_to_string()));
125 let interned_name
= token
::get_ident(ident
);
126 let name
= &interned_name
[..];
128 panictry
!(p
.expect(&token
::Eq
));
129 let e
= p
.parse_expr();
130 match names
.get(name
) {
134 &format
!("duplicate argument named `{}`",
136 ecx
.parse_sess
.span_diagnostic
.span_note(prev
.span
, "previously here");
140 order
.push(name
.to_string());
141 names
.insert(name
.to_string(), e
);
143 args
.push(p
.parse_expr());
146 Some((fmtstr
, args
, order
, names
))
149 impl<'a
, 'b
> Context
<'a
, 'b
> {
150 /// Verifies one piece of a parse string. All errors are not emitted as
151 /// fatal so we can continue giving errors about this and possibly other
153 fn verify_piece(&mut self, p
: &parse
::Piece
) {
155 parse
::String(..) => {}
156 parse
::NextArgument(ref arg
) => {
157 // width/precision first, if they have implicit positional
158 // parameters it makes more sense to consume them first.
159 self.verify_count(arg
.format
.width
);
160 self.verify_count(arg
.format
.precision
);
162 // argument second, if it's an implicit positional parameter
163 // it's written second, so it should come after width/precision.
164 let pos
= match arg
.position
{
165 parse
::ArgumentNext
=> {
166 let i
= self.next_arg
;
167 if self.check_positional_ok() {
172 parse
::ArgumentIs(i
) => Exact(i
),
173 parse
::ArgumentNamed(s
) => Named(s
.to_string()),
176 let ty
= Known(arg
.format
.ty
.to_string());
177 self.verify_arg_type(pos
, ty
);
182 fn verify_count(&mut self, c
: parse
::Count
) {
184 parse
::CountImplied
| parse
::CountIs(..) => {}
185 parse
::CountIsParam(i
) => {
186 self.verify_arg_type(Exact(i
), Unsigned
);
188 parse
::CountIsName(s
) => {
189 self.verify_arg_type(Named(s
.to_string()), Unsigned
);
191 parse
::CountIsNextParam
=> {
192 if self.check_positional_ok() {
193 let next_arg
= self.next_arg
;
194 self.verify_arg_type(Exact(next_arg
), Unsigned
);
201 fn check_positional_ok(&mut self) -> bool
{
202 if self.nest_level
!= 0 {
203 self.ecx
.span_err(self.fmtsp
, "cannot use implicit positional \
204 arguments nested inside methods");
211 fn describe_num_args(&self) -> String
{
212 match self.args
.len() {
213 0 => "no arguments given".to_string(),
214 1 => "there is 1 argument".to_string(),
215 x
=> format
!("there are {} arguments", x
),
219 fn verify_arg_type(&mut self, arg
: Position
, ty
: ArgumentType
) {
222 if self.args
.len() <= arg
{
223 let msg
= format
!("invalid reference to argument `{}` ({})",
224 arg
, self.describe_num_args());
226 self.ecx
.span_err(self.fmtsp
, &msg
[..]);
230 let arg_type
= match self.arg_types
[arg
] {
232 Some(ref x
) => Some(x
)
234 self.verify_same(self.args
[arg
].span
, &ty
, arg_type
);
236 if self.arg_types
[arg
].is_none() {
237 self.arg_types
[arg
] = Some(ty
);
242 let span
= match self.names
.get(&name
) {
245 let msg
= format
!("there is no argument named `{}`", name
);
246 self.ecx
.span_err(self.fmtsp
, &msg
[..]);
250 self.verify_same(span
, &ty
, self.name_types
.get(&name
));
251 if !self.name_types
.contains_key(&name
) {
252 self.name_types
.insert(name
.clone(), ty
);
254 // Assign this named argument a slot in the arguments array if
255 // it hasn't already been assigned a slot.
256 if !self.name_positions
.contains_key(&name
) {
257 let slot
= self.name_positions
.len();
258 self.name_positions
.insert(name
, slot
);
264 /// When we're keeping track of the types that are declared for certain
265 /// arguments, we assume that `None` means we haven't seen this argument
266 /// yet, `Some(None)` means that we've seen the argument, but no format was
267 /// specified, and `Some(Some(x))` means that the argument was declared to
270 /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
271 /// that: `Some(None) == Some(Some(x))`
272 fn verify_same(&self,
275 before
: Option
<&ArgumentType
>) {
276 let cur
= match before
{
284 (&Known(ref cur
), &Known(ref ty
)) => {
285 self.ecx
.span_err(sp
,
286 &format
!("argument redeclared with type `{}` when \
287 it was previously `{}`",
291 (&Known(ref cur
), _
) => {
292 self.ecx
.span_err(sp
,
293 &format
!("argument used to format with `{}` was \
294 attempted to not be used for formatting",
297 (_
, &Known(ref ty
)) => {
298 self.ecx
.span_err(sp
,
299 &format
!("argument previously used as a format \
300 argument attempted to be used as `{}`",
304 self.ecx
.span_err(sp
, "argument declared with multiple formats");
309 fn rtpath(ecx
: &ExtCtxt
, s
: &str) -> Vec
<ast
::Ident
> {
310 vec
![ecx
.ident_of_std("core"), ecx
.ident_of("fmt"), ecx
.ident_of("rt"),
311 ecx
.ident_of("v1"), ecx
.ident_of(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
<_
> = repeat(None
).take(self.name_positions
.len()).collect();
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 self.ecx
.expr_call_global(self.macsp
, vec
!(
585 self.ecx
.ident_of_std("core"),
586 self.ecx
.ident_of("fmt"),
587 self.ecx
.ident_of("Arguments"),
588 self.ecx
.ident_of(fn_name
)), fn_args
)
591 fn format_arg(ecx
: &ExtCtxt
, macsp
: Span
, sp
: Span
,
592 ty
: &ArgumentType
, arg
: P
<ast
::Expr
>)
594 let trait_
= match *ty
{
595 Known(ref tyname
) => {
608 &format
!("unknown format trait `{}`",
615 return ecx
.expr_call_global(macsp
, vec
![
616 ecx
.ident_of_std("core"),
618 ecx
.ident_of("ArgumentV1"),
619 ecx
.ident_of("from_usize")], vec
![arg
])
623 let format_fn
= ecx
.path_global(sp
, vec
![
624 ecx
.ident_of_std("core"),
626 ecx
.ident_of(trait_
),
627 ecx
.ident_of("fmt")]);
628 ecx
.expr_call_global(macsp
, vec
![
629 ecx
.ident_of_std("core"),
631 ecx
.ident_of("ArgumentV1"),
632 ecx
.ident_of("new")], vec
![arg
, ecx
.expr_path(format_fn
)])
636 pub fn expand_format_args
<'cx
>(ecx
: &'cx
mut ExtCtxt
, sp
: Span
,
637 tts
: &[ast
::TokenTree
])
638 -> Box
<base
::MacResult
+'cx
> {
640 match parse_args(ecx
, sp
, tts
) {
641 Some((efmt
, args
, order
, names
)) => {
642 MacEager
::expr(expand_preparsed_format_args(ecx
, sp
, efmt
,
645 None
=> DummyResult
::expr(sp
)
649 /// Take the various parts of `format_args!(efmt, args..., name=names...)`
650 /// and construct the appropriate formatting expression.
651 pub fn expand_preparsed_format_args(ecx
: &mut ExtCtxt
, sp
: Span
,
653 args
: Vec
<P
<ast
::Expr
>>,
654 name_ordering
: Vec
<String
>,
655 names
: HashMap
<String
, P
<ast
::Expr
>>)
657 let arg_types
: Vec
<_
> = (0..args
.len()).map(|_
| None
).collect();
658 let macsp
= ecx
.call_site();
659 // Expand the format literal so that efmt.span will have a backtrace. This
660 // is essential for locating a bug when the format literal is generated in
661 // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
662 let efmt
= ecx
.expander().fold_expr(efmt
);
663 let mut cx
= Context
{
666 arg_types
: arg_types
,
668 name_positions
: HashMap
::new(),
669 name_types
: HashMap
::new(),
670 name_ordering
: name_ordering
,
673 literal
: String
::new(),
675 str_pieces
: Vec
::new(),
676 all_pieces_simple
: true,
680 let fmt
= match expr_to_string(cx
.ecx
,
682 "format argument must be a string literal.") {
683 Some((fmt
, _
)) => fmt
,
684 None
=> return DummyResult
::raw_expr(sp
)
687 let mut parser
= parse
::Parser
::new(&fmt
);
690 match parser
.next() {
692 if !parser
.errors
.is_empty() { break }
693 cx
.verify_piece(&piece
);
694 match cx
.trans_piece(&piece
) {
696 let s
= cx
.trans_literal_string();
697 cx
.str_pieces
.push(s
);
698 cx
.pieces
.push(piece
);
706 if !parser
.errors
.is_empty() {
707 cx
.ecx
.span_err(cx
.fmtsp
, &format
!("invalid format string: {}",
708 parser
.errors
.remove(0)));
709 return DummyResult
::raw_expr(sp
);
711 if !cx
.literal
.is_empty() {
712 let s
= cx
.trans_literal_string();
713 cx
.str_pieces
.push(s
);
716 // Make sure that all arguments were used and all arguments have types.
717 for (i
, ty
) in cx
.arg_types
.iter().enumerate() {
719 cx
.ecx
.span_err(cx
.args
[i
].span
, "argument never used");
722 for (name
, e
) in &cx
.names
{
723 if !cx
.name_types
.contains_key(name
) {
724 cx
.ecx
.span_err(e
.span
, "named argument never used");