summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-12-28 03:11:48 +0000
committerbors <bors@rust-lang.org>2014-12-28 03:11:48 +0000
commit3e6b29f8ad1ddfcb134d743a66ee5f467e16c350 (patch)
treedfd36279301f37402264995a2d25a5d21eb38464 /src/libsyntax
parent070ab63807dc80fa6a6c5ee80531284761ab42de (diff)
parent647e54d6d154e1a267e84c8ae9f1315e3f9b93fc (diff)
downloadrust-3e6b29f8ad1ddfcb134d743a66ee5f467e16c350.tar.gz
rust-3e6b29f8ad1ddfcb134d743a66ee5f467e16c350.zip
auto merge of #20136 : eddyb/rust/format-args, r=alexcrichton
We have the technology: no longer do you need to write closures to use `format_args!`.
This is a `[breaking-change]`, as it forces you to clean up old hacks - if you had code like this:
```rust
format_args!(fmt::format, "{} {} {}", a, b, c)
format_args!(|args| { w.write_fmt(args) }, "{} {} {}", x, y, z)
```
change it to this: 
```rust
fmt::format(format_args!("{} {} {}", a, b, c))
w.write_fmt(format_args!("{} {} {}", x, y, z))
```
To allow them to be called with `format_args!(...)` directly, several functions were modified to
take `fmt::Arguments` by value instead of by reference. Also, `fmt::Arguments` derives `Copy`
now in order to preserve all usecases that were previously possible.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/deriving/show.rs10
-rw-r--r--src/libsyntax/ext/format.rs236
2 files changed, 99 insertions, 147 deletions
diff --git a/src/libsyntax/ext/deriving/show.rs b/src/libsyntax/ext/deriving/show.rs
index 19b45a1e610..2788c89676a 100644
--- a/src/libsyntax/ext/deriving/show.rs
+++ b/src/libsyntax/ext/deriving/show.rs
@@ -131,8 +131,10 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
     let format_string = cx.expr_str(span, s);
 
     // phew, not our responsibility any more!
-    format::expand_preparsed_format_args(cx, span,
-                                         format::MethodCall(formatter, meth),
-                                         format_string, exprs, Vec::new(),
-                                         HashMap::new())
+
+    let args = vec![
+        format::expand_preparsed_format_args(cx, span, format_string,
+                                             exprs, vec![], HashMap::new())
+    ];
+    cx.expr_method_call(span, formatter, meth, args)
 }
diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs
index aad4045f00a..6474d92953f 100644
--- a/src/libsyntax/ext/format.rs
+++ b/src/libsyntax/ext/format.rs
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub use self::Invocation::*;
 use self::ArgumentType::*;
 use self::Position::*;
 
@@ -23,17 +22,16 @@ use parse::token;
 use ptr::P;
 
 use std::collections::HashMap;
-use std::string;
 
 #[deriving(PartialEq)]
 enum ArgumentType {
-    Known(string::String),
+    Known(String),
     Unsigned
 }
 
 enum Position {
     Exact(uint),
-    Named(string::String),
+    Named(String),
 }
 
 struct Context<'a, 'b:'a> {
@@ -48,12 +46,12 @@ struct Context<'a, 'b:'a> {
     /// Note that we keep a side-array of the ordering of the named arguments
     /// found to be sure that we can translate them in the same order that they
     /// were declared in.
-    names: HashMap<string::String, P<ast::Expr>>,
-    name_types: HashMap<string::String, ArgumentType>,
-    name_ordering: Vec<string::String>,
+    names: HashMap<String, P<ast::Expr>>,
+    name_types: HashMap<String, ArgumentType>,
+    name_ordering: Vec<String>,
 
     /// The latest consecutive literal strings, or empty if there weren't any.
-    literal: string::String,
+    literal: String,
 
     /// Collection of the compiled `rt::Argument` structures
     pieces: Vec<P<ast::Expr>>,
@@ -62,62 +60,40 @@ struct Context<'a, 'b:'a> {
     /// Stays `true` if all formatting parameters are default (as in "{}{}").
     all_pieces_simple: bool,
 
-    name_positions: HashMap<string::String, uint>,
-    method_statics: Vec<P<ast::Item>>,
+    name_positions: HashMap<String, uint>,
 
     /// Updated as arguments are consumed or methods are entered
     nest_level: uint,
     next_arg: uint,
 }
 
-pub enum Invocation {
-    Call(P<ast::Expr>),
-    MethodCall(P<ast::Expr>, ast::Ident),
-}
-
 /// Parses the arguments from the given list of tokens, returning None
 /// if there's a parse error so we can continue parsing other format!
 /// expressions.
 ///
-/// If parsing succeeds, the second return value is:
+/// If parsing succeeds, the return value is:
 ///
 ///     Some((fmtstr, unnamed arguments, ordering of named arguments,
 ///           named arguments))
-fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
-              tts: &[ast::TokenTree])
-    -> (Invocation, Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<string::String>,
-                            HashMap<string::String, P<ast::Expr>>)>) {
+fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+              -> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<String>,
+                         HashMap<String, P<ast::Expr>>)> {
     let mut args = Vec::new();
-    let mut names = HashMap::<string::String, P<ast::Expr>>::new();
+    let mut names = HashMap::<String, P<ast::Expr>>::new();
     let mut order = Vec::new();
 
     let mut p = ecx.new_parser_from_tts(tts);
-    // Parse the leading function expression (maybe a block, maybe a path)
-    let invocation = if allow_method {
-        let e = p.parse_expr();
-        if !p.eat(&token::Comma) {
-            ecx.span_err(sp, "expected token: `,`");
-            return (Call(e), None);
-        }
-        MethodCall(e, p.parse_ident())
-    } else {
-        Call(p.parse_expr())
-    };
-    if !p.eat(&token::Comma) {
-        ecx.span_err(sp, "expected token: `,`");
-        return (invocation, None);
-    }
 
     if p.token == token::Eof {
         ecx.span_err(sp, "requires at least a format string argument");
-        return (invocation, None);
+        return None;
     }
     let fmtstr = p.parse_expr();
     let mut named = false;
     while p.token != token::Eof {
         if !p.eat(&token::Comma) {
             ecx.span_err(sp, "expected token: `,`");
-            return (invocation, None);
+            return None;
         }
         if p.token == token::Eof { break } // accept trailing commas
         if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
@@ -131,13 +107,13 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
                     ecx.span_err(p.span,
                                  "expected ident, positional arguments \
                                  cannot follow named arguments");
-                    return (invocation, None);
+                    return None;
                 }
                 _ => {
                     ecx.span_err(p.span,
                                  format!("expected ident for named argument, found `{}`",
                                          p.this_token_to_string())[]);
-                    return (invocation, None);
+                    return None;
                 }
             };
             let interned_name = token::get_ident(ident);
@@ -160,7 +136,7 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
             args.push(p.parse_expr());
         }
     }
-    return (invocation, Some((fmtstr, args, order, names)));
+    Some((fmtstr, args, order, names))
 }
 
 impl<'a, 'b> Context<'a, 'b> {
@@ -225,7 +201,7 @@ impl<'a, 'b> Context<'a, 'b> {
         }
     }
 
-    fn describe_num_args(&self) -> string::String {
+    fn describe_num_args(&self) -> String {
         match self.args.len() {
             0 => "no arguments given".to_string(),
             1 => "there is 1 argument".to_string(),
@@ -474,70 +450,50 @@ impl<'a, 'b> Context<'a, 'b> {
         }
     }
 
-    fn item_static_array(ecx: &mut ExtCtxt,
-                         name: ast::Ident,
-                         piece_ty: P<ast::Ty>,
-                         pieces: Vec<P<ast::Expr>>)
-                         -> P<ast::Stmt> {
+    fn static_array(ecx: &mut ExtCtxt,
+                    name: &str,
+                    piece_ty: P<ast::Ty>,
+                    pieces: Vec<P<ast::Expr>>)
+                    -> P<ast::Expr> {
         let fmtsp = piece_ty.span;
-        let fmt = ecx.expr_vec(fmtsp, pieces);
-        let fmt = ecx.expr_addr_of(fmtsp, fmt);
-        let ty = ast::TyVec(piece_ty);
-        let ty = ast::TyRptr(Some(ecx.lifetime(fmtsp, special_idents::static_lifetime.name)),
-                             ast::MutTy{ mutbl: ast::MutImmutable, ty: ecx.ty(fmtsp, ty) });
-        let ty = ecx.ty(fmtsp, ty);
-        let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
+        let ty = ecx.ty_rptr(fmtsp,
+            ecx.ty(fmtsp, ast::TyVec(piece_ty)),
+            Some(ecx.lifetime(fmtsp, special_idents::static_lifetime.name)),
+            ast::MutImmutable);
+        let slice = ecx.expr_vec_slice(fmtsp, pieces);
+        let st = ast::ItemStatic(ty, ast::MutImmutable, slice);
+
+        let name = ecx.ident_of(name);
         let item = ecx.item(fmtsp, name, Context::static_attrs(ecx, fmtsp), st);
         let decl = respan(fmtsp, ast::DeclItem(item));
-        P(respan(fmtsp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
+
+        // Wrap the declaration in a block so that it forms a single expression.
+        ecx.expr_block(ecx.block(fmtsp,
+            vec![P(respan(fmtsp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))],
+            Some(ecx.expr_ident(fmtsp, name))))
     }
 
     /// Actually builds the expression which the iformat! block will be expanded
     /// to
-    fn to_expr(mut self, invocation: Invocation) -> P<ast::Expr> {
-        let mut lets = Vec::new();
+    fn into_expr(mut self) -> P<ast::Expr> {
         let mut locals = Vec::new();
         let mut names = Vec::from_fn(self.name_positions.len(), |_| None);
         let mut pats = Vec::new();
         let mut heads = Vec::new();
 
-        // First, declare all of our methods that are statics
-        for method in self.method_statics.into_iter() {
-            let decl = respan(self.fmtsp, ast::DeclItem(method));
-            lets.push(P(respan(self.fmtsp,
-                               ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))));
-        }
-
-        // Next, build up the static array which will become our precompiled
+        // First, build up the static array which will become our precompiled
         // format "string"
-        let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
-        let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name);
+        let static_lifetime = self.ecx.lifetime(self.fmtsp, special_idents::static_lifetime.name);
         let piece_ty = self.ecx.ty_rptr(
                 self.fmtsp,
                 self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
                 Some(static_lifetime),
                 ast::MutImmutable);
-        lets.push(Context::item_static_array(self.ecx,
-                                             static_str_name,
-                                             piece_ty,
-                                             self.str_pieces));
-
-        // Then, build up the static array which will store our precompiled
-        // nonstandard placeholders, if there are any.
-        let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
-        if !self.all_pieces_simple {
-            let piece_ty = self.ecx.ty_path(self.ecx.path_all(
-                    self.fmtsp,
-                    true, Context::rtpath(self.ecx, "Argument"),
-                    vec![static_lifetime],
-                    vec![],
-                    vec![]
-                ));
-            lets.push(Context::item_static_array(self.ecx,
-                                                 static_args_name,
-                                                 piece_ty,
-                                                 self.pieces));
-        }
+        let pieces = Context::static_array(self.ecx,
+                                           "__STATIC_FMTSTR",
+                                           piece_ty,
+                                           self.str_pieces);
+
 
         // Right now there is a bug such that for the expression:
         //      foo(bar(&1))
@@ -580,71 +536,68 @@ impl<'a, 'b> Context<'a, 'b> {
         // Now create a vector containing all the arguments
         let args = locals.into_iter().chain(names.into_iter().map(|a| a.unwrap()));
 
-        // Now create the fmt::Arguments struct with all our locals we created.
-        let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
-        let args_slice = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
-
-        let (fn_name, fn_args) = if self.all_pieces_simple {
-            ("new", vec![pieces, args_slice])
-        } else {
-            let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
-            ("with_placeholders", vec![pieces, fmt, args_slice])
-        };
-
-        let result = self.ecx.expr_call_global(self.fmtsp, vec!(
-                self.ecx.ident_of("std"),
-                self.ecx.ident_of("fmt"),
-                self.ecx.ident_of("Arguments"),
-                self.ecx.ident_of(fn_name)), fn_args);
-
-        let result = match invocation {
-            Call(e) => {
-                let span = e.span;
-                self.ecx.expr_call(span, e, vec![
-                    self.ecx.expr_addr_of(span, result)
-                ])
-            }
-            MethodCall(e, m) => {
-                let span = e.span;
-                self.ecx.expr_method_call(span, e, m, vec![
-                    self.ecx.expr_addr_of(span, result)
-                ])
-            }
-        };
-        let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
-                                                      Some(result)));
+        let args_array = self.ecx.expr_vec(self.fmtsp, args.collect());
 
         // Constructs an AST equivalent to:
         //
         //      match (&arg0, &arg1) {
-        //          (tmp0, tmp1) => body
+        //          (tmp0, tmp1) => args_array
         //      }
         //
         // It was:
         //
         //      let tmp0 = &arg0;
         //      let tmp1 = &arg1;
-        //      body
+        //      args_array
         //
         // Because of #11585 the new temporary lifetime rule, the enclosing
         // statements for these temporaries become the let's themselves.
         // If one or more of them are RefCell's, RefCell borrow() will also
-        // end there; they don't last long enough for body to use them. The
-        // match expression solves the scope problem.
+        // end there; they don't last long enough for args_array to use them.
+        // The match expression solves the scope problem.
         //
         // Note, it may also very well be transformed to:
         //
         //      match arg0 {
         //          ref tmp0 => {
         //              match arg1 => {
-        //                  ref tmp1 => body } } }
+        //                  ref tmp1 => args_array } } }
         //
         // But the nested match expression is proved to perform not as well
         // as series of let's; the first approach does.
         let pat = self.ecx.pat_tuple(self.fmtsp, pats);
-        let arm = self.ecx.arm(self.fmtsp, vec!(pat), body);
+        let arm = self.ecx.arm(self.fmtsp, vec!(pat), args_array);
         let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
-        self.ecx.expr_match(self.fmtsp, head, vec!(arm))
+        let result = self.ecx.expr_match(self.fmtsp, head, vec!(arm));
+
+        let args_slice = self.ecx.expr_addr_of(self.fmtsp, result);
+
+        // Now create the fmt::Arguments struct with all our locals we created.
+        let (fn_name, fn_args) = if self.all_pieces_simple {
+            ("new", vec![pieces, args_slice])
+        } else {
+            // Build up the static array which will store our precompiled
+            // nonstandard placeholders, if there are any.
+            let piece_ty = self.ecx.ty_path(self.ecx.path_all(
+                    self.fmtsp,
+                    true, Context::rtpath(self.ecx, "Argument"),
+                    vec![static_lifetime],
+                    vec![],
+                    vec![]
+                ));
+            let fmt = Context::static_array(self.ecx,
+                                            "__STATIC_FMTARGS",
+                                            piece_ty,
+                                            self.pieces);
+
+            ("with_placeholders", vec![pieces, fmt, args_slice])
+        };
+
+        self.ecx.expr_call_global(self.fmtsp, vec!(
+                self.ecx.ident_of("std"),
+                self.ecx.ident_of("fmt"),
+                self.ecx.ident_of("Arguments"),
+                self.ecx.ident_of(fn_name)), fn_args)
     }
 
     fn format_arg(ecx: &ExtCtxt, sp: Span,
@@ -694,24 +647,22 @@ pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
                                tts: &[ast::TokenTree])
                                -> Box<base::MacResult+'cx> {
 
-    match parse_args(ecx, sp, false, tts) {
-        (invocation, Some((efmt, args, order, names))) => {
-            MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
+    match parse_args(ecx, sp, tts) {
+        Some((efmt, args, order, names)) => {
+            MacExpr::new(expand_preparsed_format_args(ecx, sp, efmt,
                                                       args, order, names))
         }
-        (_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
+        None => DummyResult::expr(sp)
     }
 }
 
-/// Take the various parts of `format_args!(extra, efmt, args...,
-/// name=names...)` and construct the appropriate formatting
-/// expression.
+/// Take the various parts of `format_args!(efmt, args..., name=names...)`
+/// and construct the appropriate formatting expression.
 pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
-                                    invocation: Invocation,
                                     efmt: P<ast::Expr>,
                                     args: Vec<P<ast::Expr>>,
-                                    name_ordering: Vec<string::String>,
-                                    names: HashMap<string::String, P<ast::Expr>>)
+                                    name_ordering: Vec<String>,
+                                    names: HashMap<String, P<ast::Expr>>)
                                     -> P<ast::Expr> {
     let arg_types = Vec::from_fn(args.len(), |_| None);
     let mut cx = Context {
@@ -724,17 +675,16 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
         name_ordering: name_ordering,
         nest_level: 0,
         next_arg: 0,
-        literal: string::String::new(),
+        literal: String::new(),
         pieces: Vec::new(),
         str_pieces: Vec::new(),
         all_pieces_simple: true,
-        method_statics: Vec::new(),
         fmtsp: sp,
     };
     cx.fmtsp = efmt.span;
     let fmt = match expr_to_string(cx.ecx,
-                                efmt,
-                                "format argument must be a string literal.") {
+                                   efmt,
+                                   "format argument must be a string literal.") {
         Some((fmt, _)) => fmt,
         None => return DummyResult::raw_expr(sp)
     };
@@ -782,5 +732,5 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
         }
     }
 
-    cx.to_expr(invocation)
+    cx.into_expr()
 }