about summary refs log tree commit diff
path: root/src/libsyntax_ext
diff options
context:
space:
mode:
authorWang Xuerui <idontknw.wang@gmail.com>2016-05-14 20:42:47 +0800
committerWang Xuerui <idontknw.wang@gmail.com>2016-07-14 03:10:45 +0800
commit5e55a4411684a9cf3932b0597607cf82433ff3ba (patch)
treeaed9cd2295885841a9b51bf24231017b2383d7f3 /src/libsyntax_ext
parenteb5417bf12ea7f709f6603308d86b61a1fc845c5 (diff)
downloadrust-5e55a4411684a9cf3932b0597607cf82433ff3ba.tar.gz
rust-5e55a4411684a9cf3932b0597607cf82433ff3ba.zip
syntax_ext: format: allow multiple formats for one argument
This commit removed the restriction of only allowing one type per argument.
This is achieved by adding mappings between macro arguments and format
placeholders, then taking the mapping into consideration when emitting
the Arguments expression.

syntax_ext: format: fix implicit positional arguments

syntax_ext: format: don't panic if no args given for implicit positional args

Check the list lengths before use.
Fixes regression of `compile-fail/macro-backtrace-println.rs`.

syntax_ext: format: also map CountIsParam indices to expanded args

syntax_ext: format: fix ICE in case of malformed format args
Diffstat (limited to 'src/libsyntax_ext')
-rw-r--r--src/libsyntax_ext/format.rs126
1 files changed, 57 insertions, 69 deletions
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index 9e6a83a1863..e6a5ff1d444 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -49,7 +49,7 @@ struct Context<'a, 'b:'a> {
     /// Named expressions are resolved early, and are appended to the end of
     /// argument expressions.
     args: Vec<P<ast::Expr>>,
-    arg_types: Vec<Option<ArgumentType>>,
+    arg_types: Vec<Vec<ArgumentType>>,
     /// Map from named arguments to their resolved indices.
     names: HashMap<String, usize>,
 
@@ -63,6 +63,13 @@ struct Context<'a, 'b:'a> {
     /// Stays `true` if all formatting parameters are default (as in "{}{}").
     all_pieces_simple: bool,
 
+    /// Mapping between positional argument references and indices into the
+    /// final generated static argument array. We record the starting indices
+    /// corresponding to each positional argument, and number of references
+    /// consumed so far for each argument, to facilitate correct `Position`
+    /// mapping in `trans_piece`.
+    arg_index_map: Vec<usize>,
+
     /// Current position of the implicit positional arg pointer, as if it
     /// still existed in this phase of processing.
     /// Used only for `all_pieces_simple` tracking in `trans_piece`.
@@ -218,16 +225,7 @@ impl<'a, 'b> Context<'a, 'b> {
                     self.ecx.span_err(self.fmtsp, &msg[..]);
                     return;
                 }
-                {
-                    let arg_type = match self.arg_types[arg] {
-                        None => None,
-                        Some(ref x) => Some(x)
-                    };
-                    self.verify_same(self.args[arg].span, &ty, arg_type);
-                }
-                if self.arg_types[arg].is_none() {
-                    self.arg_types[arg] = Some(ty);
-                }
+                self.arg_types[arg].push(ty);
             }
 
             Named(name) => {
@@ -245,48 +243,17 @@ impl<'a, 'b> Context<'a, 'b> {
         }
     }
 
-    /// When we're keeping track of the types that are declared for certain
-    /// arguments, we assume that `None` means we haven't seen this argument
-    /// yet, `Some(None)` means that we've seen the argument, but no format was
-    /// specified, and `Some(Some(x))` means that the argument was declared to
-    /// have type `x`.
-    ///
-    /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
-    /// that: `Some(None) == Some(Some(x))`
-    fn verify_same(&self,
-                   sp: Span,
-                   ty: &ArgumentType,
-                   before: Option<&ArgumentType>) {
-        let cur = match before {
-            None => return,
-            Some(t) => t,
-        };
-        if *ty == *cur {
-            return
-        }
-        match (cur, ty) {
-            (&Known(ref cur), &Known(ref ty)) => {
-                self.ecx.span_err(sp,
-                                  &format!("argument redeclared with type `{}` when \
-                                           it was previously `{}`",
-                                          *ty,
-                                          *cur));
-            }
-            (&Known(ref cur), _) => {
-                self.ecx.span_err(sp,
-                                  &format!("argument used to format with `{}` was \
-                                           attempted to not be used for formatting",
-                                           *cur));
-            }
-            (_, &Known(ref ty)) => {
-                self.ecx.span_err(sp,
-                                  &format!("argument previously used as a format \
-                                           argument attempted to be used as `{}`",
-                                           *ty));
-            }
-            (_, _) => {
-                self.ecx.span_err(sp, "argument declared with multiple formats");
-            }
+    // NOTE: Keep the ordering the same as `into_expr`'s expansion would do!
+    fn build_index_map(&mut self) {
+        let args_len = self.args.len();
+        self.arg_index_map.reserve(args_len);
+
+        let mut sofar = 0usize;
+
+        // Generate mapping for positional args
+        for i in 0..args_len {
+            self.arg_index_map.push(sofar);
+            sofar += self.arg_types[i].len();
         }
     }
 
@@ -294,7 +261,9 @@ impl<'a, 'b> Context<'a, 'b> {
         ecx.std_path(&["fmt", "rt", "v1", s])
     }
 
-    fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
+    fn trans_count(&self,
+                   c: parse::Count,
+                   arg_index_consumed: &mut Vec<usize>) -> P<ast::Expr> {
         let sp = self.macsp;
         let count = |c, arg| {
             let mut path = Context::rtpath(self.ecx, "Count");
@@ -307,7 +276,11 @@ impl<'a, 'b> Context<'a, 'b> {
         match c {
             parse::CountIs(i) => count("Is", Some(self.ecx.expr_usize(sp, i))),
             parse::CountIsParam(i) => {
-                count("Param", Some(self.ecx.expr_usize(sp, i)))
+                // This needs mapping too, as `i` is referring to a macro
+                // argument.
+                let arg_idx = self.arg_index_map[i] + arg_index_consumed[i];
+                arg_index_consumed[i] += 1;
+                count("Param", Some(self.ecx.expr_usize(sp, arg_idx)))
             }
             parse::CountImplied => count("Implied", None),
             // should never be the case, names are already resolved
@@ -325,7 +298,10 @@ impl<'a, 'b> Context<'a, 'b> {
 
     /// Translate a `parse::Piece` to a static `rt::Argument` or append
     /// to the `literal` string.
-    fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> {
+    fn trans_piece(&mut self,
+                   piece: &parse::Piece,
+                   arg_index_consumed: &mut Vec<usize>)
+                   -> Option<P<ast::Expr>> {
         let sp = self.macsp;
         match *piece {
             parse::String(s) => {
@@ -349,7 +325,18 @@ impl<'a, 'b> Context<'a, 'b> {
                         }
                     };
                     match arg.position {
-                        parse::ArgumentIs(i) => pos("At", Some(i)),
+                        parse::ArgumentIs(i) => {
+                            // Map to index in final generated argument array
+                            // in case of multiple types specified
+                            let arg_idx = if self.args.len() > i {
+                                let arg_idx = self.arg_index_map[i] + arg_index_consumed[i];
+                                arg_index_consumed[i] += 1;
+                                arg_idx
+                            } else {
+                                0 // error already emitted elsewhere
+                            };
+                            pos("At", Some(arg_idx))
+                        }
 
                         // should never be the case, because names are already
                         // resolved.
@@ -396,8 +383,8 @@ impl<'a, 'b> Context<'a, 'b> {
                 };
                 let align = self.ecx.expr_path(align);
                 let flags = self.ecx.expr_u32(sp, arg.format.flags);
-                let prec = self.trans_count(arg.format.precision);
-                let width = self.trans_count(arg.format.width);
+                let prec = self.trans_count(arg.format.precision, arg_index_consumed);
+                let width = self.trans_count(arg.format.width, arg_index_consumed);
                 let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
                 let fmt = self.ecx.expr_struct(sp, path, vec!(
                     self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
@@ -469,15 +456,12 @@ impl<'a, 'b> Context<'a, 'b> {
         // of each variable because we don't want to move out of the arguments
         // passed to this function.
         for (i, e) in self.args.into_iter().enumerate() {
-            let arg_ty = match self.arg_types[i].as_ref() {
-                Some(ty) => ty,
-                None => continue // error already generated
-            };
-
             let name = self.ecx.ident_of(&format!("__arg{}", i));
             pats.push(self.ecx.pat_ident(DUMMY_SP, name));
-            locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
-                                            self.ecx.expr_ident(e.span, name)));
+            for ref arg_ty in self.arg_types[i].iter() {
+                locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
+                                                self.ecx.expr_ident(e.span, name)));
+            }
             heads.push(self.ecx.expr_addr_of(e.span, e));
         }
 
@@ -597,7 +581,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
                                     args: Vec<P<ast::Expr>>,
                                     names: HashMap<String, usize>)
                                     -> P<ast::Expr> {
-    let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect();
+    let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
     let macsp = ecx.call_site();
     // Expand the format literal so that efmt.span will have a backtrace. This
     // is essential for locating a bug when the format literal is generated in
@@ -609,6 +593,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
         arg_types: arg_types,
         names: names,
         curarg: 0,
+        arg_index_map: Vec::new(),
         literal: String::new(),
         pieces: Vec::new(),
         str_pieces: Vec::new(),
@@ -638,8 +623,11 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
         }
     }
 
+    cx.build_index_map();
+
+    let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()];
     for piece in pieces {
-        if let Some(piece) = cx.trans_piece(&piece) {
+        if let Some(piece) = cx.trans_piece(&piece, &mut arg_index_consumed) {
             let s = cx.trans_literal_string();
             cx.str_pieces.push(s);
             cx.pieces.push(piece);
@@ -659,7 +647,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
     // Make sure that all arguments were used and all arguments have types.
     let num_pos_args = cx.args.len() - cx.names.len();
     for (i, ty) in cx.arg_types.iter().enumerate() {
-        if ty.is_none() {
+        if ty.len() == 0 {
             let msg = if i >= num_pos_args {
                 // named argument
                 "named argument never used"