From 30d755957a0f2cc3be3b355671da79cdf34fd50a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 17 Jun 2013 17:23:18 -0700 Subject: [PATCH] Expand the deriving(ToStr) implementation --- doc/rust.md | 4 +- src/libsyntax/ext/deriving/to_str.rs | 75 ++++++++++++++++++++++++---- src/test/run-pass/deriving-to-str.rs | 68 +++++++++++++------------ 3 files changed, 102 insertions(+), 45 deletions(-) diff --git a/doc/rust.md b/doc/rust.md index 9edbc44d6c2..4eedcb9a952 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -1562,7 +1562,9 @@ Supported traits for `deriving` are: * `IterBytes`, to iterate over the bytes in a data type. * `Rand`, to create a random instance of a data type. * `ToStr`, to convert to a string. For a type with this instance, - `obj.to_str()` has the same output as `fmt!("%?", obj)`. + `obj.to_str()` has similar output as `fmt!("%?", obj)`, but it differs in that + each constituent field of the type must also implement `ToStr` and will have + `field.to_str()` invoked to build up the result. # Statements and expressions diff --git a/src/libsyntax/ext/deriving/to_str.rs b/src/libsyntax/ext/deriving/to_str.rs index 41be3a775c1..4cd168b12c0 100644 --- a/src/libsyntax/ext/deriving/to_str.rs +++ b/src/libsyntax/ext/deriving/to_str.rs @@ -10,6 +10,7 @@ use core::prelude::*; +use ast; use ast::{meta_item, item, expr}; use codemap::span; use ext::base::ExtCtxt; @@ -40,16 +41,68 @@ pub fn expand_deriving_to_str(cx: @ExtCtxt, trait_def.expand(cx, span, mitem, in_items) } -fn to_str_substructure(cx: @ExtCtxt, span: span, substr: &Substructure) -> @expr { - match substr.self_args { - [self_obj] => { - let self_addr = cx.expr_addr_of(span, self_obj); - cx.expr_call_global(span, - ~[cx.ident_of("std"), - cx.ident_of("sys"), - cx.ident_of("log_str")], - ~[self_addr]) +// It used to be the case that this deriving implementation invoked +// std::sys::log_str, but this isn't sufficient because it doesn't invoke the +// to_str() method on each field. Hence we mirror the logic of the log_str() +// method, but with tweaks to call to_str() on sub-fields. +fn to_str_substructure(cx: @ExtCtxt, span: span, + substr: &Substructure) -> @expr { + let to_str = cx.ident_of("to_str"); + + let doit = |start: &str, end: @str, name: ast::ident, + fields: &[(Option, @expr, ~[@expr])]| { + if fields.len() == 0 { + cx.expr_str_uniq(span, cx.str_of(name)) + } else { + let buf = cx.ident_of("buf"); + let start = cx.str_of(name) + start; + let init = cx.expr_str_uniq(span, start.to_managed()); + let mut stmts = ~[cx.stmt_let(span, true, buf, init)]; + let push_str = cx.ident_of("push_str"); + + let push = |s: @expr| { + let ebuf = cx.expr_ident(span, buf); + let call = cx.expr_method_call(span, ebuf, push_str, ~[s]); + stmts.push(cx.stmt_expr(call)); + }; + + for fields.iter().enumerate().advance |(i, &(name, e, _))| { + if i > 0 { + push(cx.expr_str(span, @", ")); + } + match name { + None => {} + Some(id) => { + let name = cx.str_of(id) + ": "; + push(cx.expr_str(span, name.to_managed())); + } + } + push(cx.expr_method_call(span, e, to_str, ~[])); + } + push(cx.expr_str(span, end)); + + cx.expr_blk(cx.blk(span, stmts, Some(cx.expr_ident(span, buf)))) } - _ => cx.span_bug(span, "Invalid number of arguments in `deriving(ToStr)`") - } + }; + + return match *substr.fields { + Struct(ref fields) => { + if fields.len() == 0 || fields[0].n0_ref().is_none() { + doit("(", @")", substr.type_ident, *fields) + } else { + doit("{", @"}", substr.type_ident, *fields) + } + } + + EnumMatching(_, variant, ref fields) => { + match variant.node.kind { + ast::tuple_variant_kind(*) => + doit("(", @")", variant.node.name, *fields), + ast::struct_variant_kind(*) => + doit("{", @"}", variant.node.name, *fields), + } + } + + _ => cx.bug("expected Struct or EnumMatching in deriving(ToStr)") + }; } diff --git a/src/test/run-pass/deriving-to-str.rs b/src/test/run-pass/deriving-to-str.rs index fcf0a009d9b..1fc1d6815f5 100644 --- a/src/test/run-pass/deriving-to-str.rs +++ b/src/test/run-pass/deriving-to-str.rs @@ -1,5 +1,4 @@ -// xfail-fast #6330 -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -9,39 +8,42 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::rand; +#[deriving(ToStr)] +enum A {} +#[deriving(ToStr)] +enum B { B1, B2, B3 } +#[deriving(ToStr)] +enum C { C1(int), C2(B), C3(~str) } +#[deriving(ToStr)] +enum D { D1{ a: int } } +#[deriving(ToStr)] +struct E; +#[deriving(ToStr)] +struct F(int); +#[deriving(ToStr)] +struct G(int, int); +#[deriving(ToStr)] +struct H { a: int } +#[deriving(ToStr)] +struct I { a: int, b: int } +#[deriving(ToStr)] +struct J(Custom); -#[deriving(Rand,ToStr)] -struct A; - -#[deriving(Rand,ToStr)] -struct B(int, int); - -#[deriving(Rand,ToStr)] -struct C { - x: f64, - y: (u8, u8) -} - -#[deriving(Rand,ToStr)] -enum D { - D0, - D1(uint), - D2 { x: (), y: () } +struct Custom; +impl ToStr for Custom { + fn to_str(&self) -> ~str { ~"yay" } } fn main() { - macro_rules! t( - ($ty:ty) => {{ - let x =rand::random::<$ty>(); - assert_eq!(x.to_str(), fmt!("%?", x)); - }} - ); - - for 20.times { - t!(A); - t!(B); - t!(C); - t!(D); - } + assert_eq!(B1.to_str(), ~"B1"); + assert_eq!(B2.to_str(), ~"B2"); + assert_eq!(C1(3).to_str(), ~"C1(3)"); + assert_eq!(C2(B2).to_str(), ~"C2(B2)"); + assert_eq!(D1{ a: 2 }.to_str(), ~"D1{a: 2}"); + assert_eq!(E.to_str(), ~"E"); + assert_eq!(F(3).to_str(), ~"F(3)"); + assert_eq!(G(3, 4).to_str(), ~"G(3, 4)"); + assert_eq!(G(3, 4).to_str(), ~"G(3, 4)"); + assert_eq!(I{ a: 2, b: 4 }.to_str(), ~"I{a: 2, b: 4}"); + assert_eq!(J(Custom).to_str(), ~"J(yay)"); }