diff options
| author | bors <bors@rust-lang.org> | 2014-12-28 03:11:48 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-12-28 03:11:48 +0000 |
| commit | 3e6b29f8ad1ddfcb134d743a66ee5f467e16c350 (patch) | |
| tree | dfd36279301f37402264995a2d25a5d21eb38464 /src/libsyntax | |
| parent | 070ab63807dc80fa6a6c5ee80531284761ab42de (diff) | |
| parent | 647e54d6d154e1a267e84c8ae9f1315e3f9b93fc (diff) | |
| download | rust-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.rs | 10 | ||||
| -rw-r--r-- | src/libsyntax/ext/format.rs | 236 |
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() } |
