about summary refs log tree commit diff
path: root/src/libsyntax_ext
diff options
context:
space:
mode:
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"