2014-02-06 23:02:28 +11:00
|
|
|
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use ast;
|
2014-09-13 19:06:01 +03:00
|
|
|
use ast::{MetaItem, Item, Expr,};
|
2014-02-06 23:02:28 +11:00
|
|
|
use codemap::Span;
|
|
|
|
use ext::format;
|
|
|
|
use ext::base::ExtCtxt;
|
|
|
|
use ext::build::AstBuilder;
|
|
|
|
use ext::deriving::generic::*;
|
2014-05-29 12:19:05 +09:00
|
|
|
use ext::deriving::generic::ty::*;
|
2014-02-06 23:02:28 +11:00
|
|
|
use parse::token;
|
2014-09-13 19:06:01 +03:00
|
|
|
use ptr::P;
|
2014-02-06 23:02:28 +11:00
|
|
|
|
2014-05-29 19:03:06 -07:00
|
|
|
use std::collections::HashMap;
|
2014-02-06 23:02:28 +11:00
|
|
|
|
2014-12-08 13:28:32 -05:00
|
|
|
pub fn expand_deriving_show<F>(cx: &mut ExtCtxt,
|
|
|
|
span: Span,
|
|
|
|
mitem: &MetaItem,
|
|
|
|
item: &Item,
|
|
|
|
push: F) where
|
|
|
|
F: FnOnce(P<Item>),
|
|
|
|
{
|
2014-02-06 23:02:28 +11:00
|
|
|
// &mut ::std::fmt::Formatter
|
2014-09-07 14:57:26 -07:00
|
|
|
let fmtr = Ptr(box Literal(path_std!(cx, core::fmt::Formatter)),
|
2014-02-06 23:02:28 +11:00
|
|
|
Borrowed(None, ast::MutMutable));
|
|
|
|
|
|
|
|
let trait_def = TraitDef {
|
2014-02-08 19:39:53 -05:00
|
|
|
span: span,
|
2014-02-28 13:09:09 -08:00
|
|
|
attributes: Vec::new(),
|
2014-09-07 14:57:26 -07:00
|
|
|
path: path_std!(cx, core::fmt::Debug),
|
2014-02-28 13:09:09 -08:00
|
|
|
additional_bounds: Vec::new(),
|
2014-02-06 23:02:28 +11:00
|
|
|
generics: LifetimeBounds::empty(),
|
2015-01-25 00:29:24 -05:00
|
|
|
methods: vec![
|
2014-02-06 23:02:28 +11:00
|
|
|
MethodDef {
|
|
|
|
name: "fmt",
|
|
|
|
generics: LifetimeBounds::empty(),
|
|
|
|
explicit_self: borrowed_explicit_self(),
|
2014-02-28 13:09:09 -08:00
|
|
|
args: vec!(fmtr),
|
2014-09-07 14:57:26 -07:00
|
|
|
ret_ty: Literal(path_std!(cx, core::fmt::Result)),
|
2014-04-23 22:43:45 +08:00
|
|
|
attributes: Vec::new(),
|
2015-02-15 09:52:21 +01:00
|
|
|
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
2014-04-21 23:25:18 -07:00
|
|
|
show_substructure(a, b, c)
|
2015-02-15 09:52:21 +01:00
|
|
|
}))
|
2014-02-06 23:02:28 +11:00
|
|
|
}
|
2015-01-25 00:29:24 -05:00
|
|
|
],
|
|
|
|
associated_types: Vec::new(),
|
2014-02-06 23:02:28 +11:00
|
|
|
};
|
2014-02-12 23:53:52 -08:00
|
|
|
trait_def.expand(cx, mitem, item, push)
|
2014-02-06 23:02:28 +11:00
|
|
|
}
|
|
|
|
|
2014-06-09 13:12:30 -07:00
|
|
|
/// We construct a format string and then defer to std::fmt, since that
|
|
|
|
/// knows what's up with formatting and so on.
|
2014-02-06 23:02:28 +11:00
|
|
|
fn show_substructure(cx: &mut ExtCtxt, span: Span,
|
2014-09-13 19:06:01 +03:00
|
|
|
substr: &Substructure) -> P<Expr> {
|
2014-02-06 23:02:28 +11:00
|
|
|
// build `<name>`, `<name>({}, {}, ...)` or `<name> { <field>: {},
|
|
|
|
// <field>: {}, ... }` based on the "shape".
|
|
|
|
//
|
|
|
|
// Easy start: they all start with the name.
|
|
|
|
let name = match *substr.fields {
|
|
|
|
Struct(_) => substr.type_ident,
|
|
|
|
EnumMatching(_, v, _) => v.node.name,
|
2014-07-07 09:13:49 +02:00
|
|
|
EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
|
2015-01-20 15:45:07 -08:00
|
|
|
cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
|
2014-02-06 23:02:28 +11:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-02-04 21:48:12 +01:00
|
|
|
let mut format_string = String::from_str(&token::get_ident(name));
|
2014-02-06 23:02:28 +11:00
|
|
|
// the internal fields we're actually formatting
|
2014-02-28 13:09:09 -08:00
|
|
|
let mut exprs = Vec::new();
|
2014-02-06 23:02:28 +11:00
|
|
|
|
|
|
|
// Getting harder... making the format string:
|
|
|
|
match *substr.fields {
|
|
|
|
// unit struct/nullary variant: no work necessary!
|
2014-02-13 09:46:46 -08:00
|
|
|
Struct(ref fields) if fields.len() == 0 => {}
|
|
|
|
EnumMatching(_, _, ref fields) if fields.len() == 0 => {}
|
2014-02-06 23:02:28 +11:00
|
|
|
|
|
|
|
Struct(ref fields) | EnumMatching(_, _, ref fields) => {
|
2014-10-14 23:05:01 -07:00
|
|
|
if fields[0].name.is_none() {
|
2014-02-06 23:02:28 +11:00
|
|
|
// tuple struct/"normal" variant
|
|
|
|
|
|
|
|
format_string.push_str("(");
|
|
|
|
|
|
|
|
for (i, field) in fields.iter().enumerate() {
|
|
|
|
if i != 0 { format_string.push_str(", "); }
|
|
|
|
|
2014-12-20 00:09:35 -08:00
|
|
|
format_string.push_str("{:?}");
|
2014-02-06 23:02:28 +11:00
|
|
|
|
2014-09-13 19:06:01 +03:00
|
|
|
exprs.push(field.self_.clone());
|
2014-02-06 23:02:28 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
format_string.push_str(")");
|
|
|
|
} else {
|
|
|
|
// normal struct/struct variant
|
|
|
|
|
2014-05-28 09:24:28 -07:00
|
|
|
format_string.push_str(" {{");
|
2014-02-06 23:02:28 +11:00
|
|
|
|
|
|
|
for (i, field) in fields.iter().enumerate() {
|
|
|
|
if i != 0 { format_string.push_str(","); }
|
|
|
|
|
2014-02-14 07:07:09 +02:00
|
|
|
let name = token::get_ident(field.name.unwrap());
|
2014-02-06 23:02:28 +11:00
|
|
|
format_string.push_str(" ");
|
2015-02-04 21:48:12 +01:00
|
|
|
format_string.push_str(&name);
|
2014-12-20 00:09:35 -08:00
|
|
|
format_string.push_str(": {:?}");
|
2014-02-06 23:02:28 +11:00
|
|
|
|
2014-09-13 19:06:01 +03:00
|
|
|
exprs.push(field.self_.clone());
|
2014-02-06 23:02:28 +11:00
|
|
|
}
|
|
|
|
|
2014-05-28 09:24:28 -07:00
|
|
|
format_string.push_str(" }}");
|
2014-02-06 23:02:28 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
|
|
|
|
// AST construction!
|
|
|
|
// we're basically calling
|
|
|
|
//
|
syntax: Add a macro, format_args_method!()
Currently, the format_args!() macro takes as its first argument an expression
which is the callee of an ExprCall. This means that if format_args!() is used
with calling a method a closure must be used. Consider this code, however:
format_args!(|args| { foo.writer.write_fmt(args) }, "{}", foo.field)
The closure borrows the entire `foo` structure, disallowing the later borrow of
`foo.field`. To preserve the semantics of the `write!` macro, it is also
impossible to borrow specifically the `writer` field of the `foo` structure
because it must be borrowed mutably, but the `foo` structure is not guaranteed
to be mutable itself.
This new macro is invoked like:
format_args_method!(foo.writer, write_fmt, "{}", foo.field)
This macro will generate an ExprMethodCall which allows the borrow checker to
understand that `writer` and `field` should be borrowed separately.
This macro is not strictly necessary, with DST or possibly UFCS other
workarounds could be used. For now, though, it looks like this is required to
implement the `write!` macro.
2014-05-10 13:53:40 -07:00
|
|
|
// format_arg_method!(fmt, write_fmt, "<format_string>", exprs...)
|
2014-02-06 23:02:28 +11:00
|
|
|
//
|
|
|
|
// but doing it directly via ext::format.
|
2014-09-13 19:06:01 +03:00
|
|
|
let formatter = substr.nonself_args[0].clone();
|
2014-02-06 23:02:28 +11:00
|
|
|
|
syntax: Add a macro, format_args_method!()
Currently, the format_args!() macro takes as its first argument an expression
which is the callee of an ExprCall. This means that if format_args!() is used
with calling a method a closure must be used. Consider this code, however:
format_args!(|args| { foo.writer.write_fmt(args) }, "{}", foo.field)
The closure borrows the entire `foo` structure, disallowing the later borrow of
`foo.field`. To preserve the semantics of the `write!` macro, it is also
impossible to borrow specifically the `writer` field of the `foo` structure
because it must be borrowed mutably, but the `foo` structure is not guaranteed
to be mutable itself.
This new macro is invoked like:
format_args_method!(foo.writer, write_fmt, "{}", foo.field)
This macro will generate an ExprMethodCall which allows the borrow checker to
understand that `writer` and `field` should be borrowed separately.
This macro is not strictly necessary, with DST or possibly UFCS other
workarounds could be used. For now, though, it looks like this is required to
implement the `write!` macro.
2014-05-10 13:53:40 -07:00
|
|
|
let meth = cx.ident_of("write_fmt");
|
2015-02-18 14:48:57 -05:00
|
|
|
let s = token::intern_and_get_ident(&format_string[..]);
|
2014-02-06 23:02:28 +11:00
|
|
|
let format_string = cx.expr_str(span, s);
|
|
|
|
|
|
|
|
// phew, not our responsibility any more!
|
2014-12-21 11:28:18 +02:00
|
|
|
|
|
|
|
let args = vec![
|
|
|
|
format::expand_preparsed_format_args(cx, span, format_string,
|
|
|
|
exprs, vec![], HashMap::new())
|
|
|
|
];
|
|
|
|
cx.expr_method_call(span, formatter, meth, args)
|
2014-02-06 23:02:28 +11:00
|
|
|
}
|