Fix #[derive] for empty structs with braces
This commit is contained in:
parent
98a59cf57e
commit
4e8e607d84
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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")
|
||||
};
|
||||
|
||||
|
45
src/test/run-pass/empty-struct-braces-derive.rs
Normal file
45
src/test/run-pass/empty-struct-braces-derive.rs
Normal 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");
|
||||
}
|
Loading…
Reference in New Issue
Block a user