Fix #[derive] for empty structs with braces

This commit is contained in:
Vadim Petrochenkov 2016-02-22 21:24:32 +03:00
parent 98a59cf57e
commit 4e8e607d84
7 changed files with 95 additions and 38 deletions

View File

@ -11,7 +11,7 @@
use deriving::generic::*;
use deriving::generic::ty::*;
use syntax::ast::{MetaItem, Expr};
use syntax::ast::{MetaItem, Expr, VariantData};
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::ext::build::AstBuilder;
@ -66,14 +66,17 @@ fn cs_clone(
cx.expr_call_global(field.span, fn_path.clone(), args)
};
let vdata;
match *substr.fields {
Struct(ref af) => {
Struct(vdata_, ref af) => {
ctor_path = cx.path(trait_span, vec![substr.type_ident]);
all_fields = af;
vdata = vdata_;
}
EnumMatching(_, variant, ref af) => {
ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]);
all_fields = af;
vdata = &variant.node.data;
},
EnumNonMatchingCollapsed (..) => {
cx.span_bug(trait_span,
@ -86,30 +89,29 @@ fn cs_clone(
}
}
if !all_fields.is_empty() && all_fields[0].name.is_none() {
// enum-like
let subcalls = all_fields.iter().map(subcall).collect();
let path = cx.expr_path(ctor_path);
cx.expr_call(trait_span, path, subcalls)
} else {
// struct-like
let fields = all_fields.iter().map(|field| {
let ident = match field.name {
Some(i) => i,
None => {
cx.span_bug(trait_span,
&format!("unnamed field in normal struct in \
`derive({})`", name))
}
};
cx.field_imm(field.span, ident, subcall(field))
}).collect::<Vec<_>>();
match *vdata {
VariantData::Struct(..) => {
let fields = all_fields.iter().map(|field| {
let ident = match field.name {
Some(i) => i,
None => {
cx.span_bug(trait_span,
&format!("unnamed field in normal struct in \
`derive({})`", name))
}
};
cx.field_imm(field.span, ident, subcall(field))
}).collect::<Vec<_>>();
if fields.is_empty() {
// no fields, so construct like `None`
cx.expr_path(ctor_path)
} else {
cx.expr_struct(trait_span, ctor_path, fields)
}
VariantData::Tuple(..) => {
let subcalls = all_fields.iter().map(subcall).collect();
let path = cx.expr_path(ctor_path);
cx.expr_call(trait_span, path, subcalls)
}
VariantData::Unit(..) => {
cx.expr_path(ctor_path)
}
}
}

View File

@ -62,7 +62,7 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
// or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
// based on the "shape".
let ident = match *substr.fields {
Struct(_) => substr.type_ident,
Struct(..) => substr.type_ident,
EnumMatching(_, v, _) => v.node.name,
EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
@ -76,11 +76,16 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
let builder_expr = cx.expr_ident(span, builder.clone());
let fmt = substr.nonself_args[0].clone();
let is_struct = match *substr.fields {
Struct(vdata, _) => vdata,
EnumMatching(_, v, _) => &v.node.data,
_ => unreachable!()
}.is_struct();
let stmts = match *substr.fields {
Struct(ref fields) | EnumMatching(_, _, ref fields) => {
Struct(_, ref fields) | EnumMatching(_, _, ref fields) => {
let mut stmts = vec![];
if fields.is_empty() || fields[0].name.is_none() {
if !is_struct {
// tuple struct/"normal" variant
let expr = cx.expr_method_call(span,
fmt,

View File

@ -179,7 +179,7 @@ fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
let encode = cx.ident_of("encode");
return match *substr.fields {
Struct(ref fields) => {
Struct(_, ref fields) => {
let emit_struct_field = cx.ident_of("emit_struct_field");
let mut stmts = Vec::new();
for (i, &FieldInfo {

View File

@ -300,7 +300,7 @@ pub enum StaticFields {
/// A summary of the possible sets of fields.
pub enum SubstructureFields<'a> {
Struct(Vec<FieldInfo<'a>>),
Struct(&'a ast::VariantData, Vec<FieldInfo<'a>>),
/// Matching variants of the enum: variant index, ast::Variant,
/// fields: the field name is only non-`None` in the case of a struct
/// variant.
@ -981,7 +981,7 @@ impl<'a> MethodDef<'a> {
type_ident,
self_args,
nonself_args,
&Struct(fields));
&Struct(struct_def, fields));
// make a series of nested matches, to destructure the
// structs. This is actually right-to-left, but it shouldn't
@ -1460,8 +1460,9 @@ impl<'a> TraitDef<'a> {
fields in generic `derive`"),
// named fields
(_, false) => Named(named_idents),
// tuple structs (includes empty structs)
(_, _) => Unnamed(just_spans)
// empty structs
_ if struct_def.is_struct() => Named(named_idents),
_ => Unnamed(just_spans),
}
}
@ -1486,7 +1487,11 @@ impl<'a> TraitDef<'a> {
P<Expr>,
&'a [ast::Attribute])>) {
if struct_def.fields().is_empty() {
return (cx.pat_enum(self.span, struct_path, vec![]), vec![]);
if struct_def.is_struct() {
return (cx.pat_struct(self.span, struct_path, vec![]), vec![]);
} else {
return (cx.pat_enum(self.span, struct_path, vec![]), vec![]);
}
}
let mut paths = Vec::new();
@ -1521,7 +1526,7 @@ impl<'a> TraitDef<'a> {
// struct_type is definitely not Unknown, since struct_def.fields
// must be nonempty to reach here
let pattern = if struct_type == Record {
let pattern = if struct_def.is_struct() {
let field_pats = subpats.into_iter().zip(&ident_expr)
.map(|(pat, &(_, id, _, _))| {
// id is guaranteed to be Some
@ -1566,7 +1571,7 @@ pub fn cs_fold<F>(use_foldl: bool,
F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>,
{
match *substructure.fields {
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
EnumMatching(_, _, ref all_fields) | Struct(_, ref all_fields) => {
if use_foldl {
all_fields.iter().fold(base, |old, field| {
f(cx,
@ -1612,7 +1617,7 @@ pub fn cs_same_method<F>(f: F,
F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>,
{
match *substructure.fields {
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
EnumMatching(_, _, ref all_fields) | Struct(_, ref all_fields) => {
// call self_n.method(other_1_n, other_2_n, ...)
let called = all_fields.iter().map(|field| {
cx.expr_method_call(field.span,

View File

@ -76,7 +76,7 @@ fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure)
let mut stmts = Vec::new();
let fields = match *substr.fields {
Struct(ref fs) => fs,
Struct(_, ref fs) => fs,
EnumMatching(index, variant, ref fs) => {
// Determine the discriminant. We will feed this value to the byte
// iteration function.

View File

@ -73,7 +73,7 @@ fn expand(cx: &mut ExtCtxt,
fn totalsum_substructure(cx: &mut ExtCtxt, trait_span: Span,
substr: &Substructure) -> P<ast::Expr> {
let fields = match *substr.fields {
Struct(ref fs) | EnumMatching(_, _, ref fs) => fs,
Struct(_, ref fs) | EnumMatching(_, _, ref fs) => fs,
_ => cx.span_bug(trait_span, "impossible substructure")
};

View File

@ -0,0 +1,45 @@
// Copyright 2015 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.
// `#[derive(Trait)]` works for empty structs/variants with braces
#![feature(braced_empty_structs)]
#![feature(rustc_private)]
extern crate serialize as rustc_serialize;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
Default, Debug, RustcEncodable, RustcDecodable)]
struct S {}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
Debug, RustcEncodable, RustcDecodable)]
enum E {
V {},
U,
}
fn main() {
let s = S {};
let s1 = s;
let s2 = s.clone();
assert_eq!(s, s1);
assert_eq!(s, s2);
assert!(!(s < s1));
assert_eq!(format!("{:?}", s), "S");
let e = E::V {};
let e1 = e;
let e2 = e.clone();
assert_eq!(e, e1);
assert_eq!(e, e2);
assert!(!(e < e1));
assert_eq!(format!("{:?}", e), "V");
}