syntax::ext: Make type errors in deriving point to the field itself.
This rearranges the deriving code so that #[deriving] a trait on a field that doesn't implement that trait will point to the field in question, e.g. struct NotEq; // doesn't implement Eq #[deriving(Eq)] struct Foo { ok: int, also_ok: ~str, bad: NotEq // error points here. } Unfortunately, this means the error is disconnected from the `deriving` itself but there's no current way to pass that information through to rustc except via the spans, at the moment. Fixes #7724.
This commit is contained in:
parent
57d1ed819b
commit
812ea9e169
13 changed files with 410 additions and 312 deletions
|
@ -94,21 +94,21 @@ fn cs_clone(
|
|||
}
|
||||
|
||||
match *all_fields {
|
||||
[(None, _, _), .. _] => {
|
||||
[FieldInfo { name: None, _ }, .. _] => {
|
||||
// enum-like
|
||||
let subcalls = all_fields.map(|&(_, self_f, _)| subcall(self_f));
|
||||
let subcalls = all_fields.map(|field| subcall(field.self_));
|
||||
cx.expr_call_ident(span, ctor_ident, subcalls)
|
||||
},
|
||||
_ => {
|
||||
// struct-like
|
||||
let fields = do all_fields.map |&(o_id, self_f, _)| {
|
||||
let ident = match o_id {
|
||||
let fields = do all_fields.map |field| {
|
||||
let ident = match field.name {
|
||||
Some(i) => i,
|
||||
None => cx.span_bug(span,
|
||||
format!("unnamed field in normal struct in `deriving({})`",
|
||||
name))
|
||||
};
|
||||
cx.field_imm(span, ident, subcall(self_f))
|
||||
cx.field_imm(span, ident, subcall(field.self_))
|
||||
};
|
||||
|
||||
if fields.is_empty() {
|
||||
|
|
|
@ -13,9 +13,7 @@ The compiler code necessary for #[deriving(Decodable)]. See
|
|||
encodable.rs for more.
|
||||
*/
|
||||
|
||||
use std::vec;
|
||||
|
||||
use ast::{MetaItem, item, Expr, MutMutable};
|
||||
use ast::{MetaItem, item, Expr, MutMutable, Ident};
|
||||
use codemap::Span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
|
@ -66,37 +64,18 @@ fn decodable_substructure(cx: @ExtCtxt, span: Span,
|
|||
return match *substr.fields {
|
||||
StaticStruct(_, ref summary) => {
|
||||
let nfields = match *summary {
|
||||
Left(n) => n, Right(ref fields) => fields.len()
|
||||
Unnamed(ref fields) => fields.len(),
|
||||
Named(ref fields) => fields.len()
|
||||
};
|
||||
let read_struct_field = cx.ident_of("read_struct_field");
|
||||
|
||||
let getarg = |name: @str, field: uint| {
|
||||
let result = do decode_static_fields(cx, span, substr.type_ident,
|
||||
summary) |span, name, field| {
|
||||
cx.expr_method_call(span, blkdecoder, read_struct_field,
|
||||
~[cx.expr_str(span, name),
|
||||
cx.expr_uint(span, field),
|
||||
lambdadecode])
|
||||
};
|
||||
|
||||
let result = match *summary {
|
||||
Left(n) => {
|
||||
if n == 0 {
|
||||
cx.expr_ident(span, substr.type_ident)
|
||||
} else {
|
||||
let mut fields = vec::with_capacity(n);
|
||||
for i in range(0, n) {
|
||||
fields.push(getarg(format!("_field{}", i).to_managed(), i));
|
||||
}
|
||||
cx.expr_call_ident(span, substr.type_ident, fields)
|
||||
}
|
||||
}
|
||||
Right(ref fields) => {
|
||||
let fields = do fields.iter().enumerate().map |(i, f)| {
|
||||
cx.field_imm(span, *f, getarg(cx.str_of(*f), i))
|
||||
}.collect();
|
||||
cx.expr_struct_ident(span, substr.type_ident, fields)
|
||||
}
|
||||
};
|
||||
|
||||
cx.expr_method_call(span, decoder, cx.ident_of("read_struct"),
|
||||
~[cx.expr_str(span, cx.str_of(substr.type_ident)),
|
||||
cx.expr_uint(span, nfields),
|
||||
|
@ -113,31 +92,13 @@ fn decodable_substructure(cx: @ExtCtxt, span: Span,
|
|||
let (name, parts) = match *f { (i, ref p) => (i, p) };
|
||||
variants.push(cx.expr_str(span, cx.str_of(name)));
|
||||
|
||||
let getarg = |field: uint| {
|
||||
let decoded = do decode_static_fields(cx, span, name,
|
||||
parts) |span, _, field| {
|
||||
cx.expr_method_call(span, blkdecoder, rvariant_arg,
|
||||
~[cx.expr_uint(span, field),
|
||||
lambdadecode])
|
||||
};
|
||||
|
||||
let decoded = match *parts {
|
||||
Left(n) => {
|
||||
if n == 0 {
|
||||
cx.expr_ident(span, name)
|
||||
} else {
|
||||
let mut fields = vec::with_capacity(n);
|
||||
for i in range(0u, n) {
|
||||
fields.push(getarg(i));
|
||||
}
|
||||
cx.expr_call_ident(span, name, fields)
|
||||
}
|
||||
}
|
||||
Right(ref fields) => {
|
||||
let fields = do fields.iter().enumerate().map |(i, f)| {
|
||||
cx.field_imm(span, *f, getarg(i))
|
||||
}.collect();
|
||||
cx.expr_struct_ident(span, name, fields)
|
||||
}
|
||||
};
|
||||
arms.push(cx.arm(span,
|
||||
~[cx.pat_lit(span, cx.expr_uint(span, i))],
|
||||
decoded));
|
||||
|
@ -158,3 +119,31 @@ fn decodable_substructure(cx: @ExtCtxt, span: Span,
|
|||
_ => cx.bug("expected StaticEnum or StaticStruct in deriving(Decodable)")
|
||||
};
|
||||
}
|
||||
|
||||
/// Create a decoder for a single enum variant/struct:
|
||||
/// - `outer_pat_ident` is the name of this enum variant/struct
|
||||
/// - `getarg` should retrieve the `uint`-th field with name `@str`.
|
||||
fn decode_static_fields(cx: @ExtCtxt, outer_span: Span, outer_pat_ident: Ident,
|
||||
fields: &StaticFields,
|
||||
getarg: &fn(Span, @str, uint) -> @Expr) -> @Expr {
|
||||
match *fields {
|
||||
Unnamed(ref fields) => {
|
||||
if fields.is_empty() {
|
||||
cx.expr_ident(outer_span, outer_pat_ident)
|
||||
} else {
|
||||
let fields = do fields.iter().enumerate().map |(i, &span)| {
|
||||
getarg(span, format!("_field{}", i).to_managed(), i)
|
||||
}.collect();
|
||||
|
||||
cx.expr_call_ident(outer_span, outer_pat_ident, fields)
|
||||
}
|
||||
}
|
||||
Named(ref fields) => {
|
||||
// use the field's span to get nicer error messages.
|
||||
let fields = do fields.iter().enumerate().map |(i, &(name, span))| {
|
||||
cx.field_imm(span, name, getarg(span, cx.str_of(name), i))
|
||||
}.collect();
|
||||
cx.expr_struct_ident(outer_span, outer_pat_ident, fields)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,6 @@ use ext::base::ExtCtxt;
|
|||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
|
||||
use std::vec;
|
||||
|
||||
pub fn expand_deriving_default(cx: @ExtCtxt,
|
||||
span: Span,
|
||||
mitem: @MetaItem,
|
||||
|
@ -47,22 +45,22 @@ fn default_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Exp
|
|||
cx.ident_of("Default"),
|
||||
cx.ident_of("default")
|
||||
];
|
||||
let default_call = cx.expr_call_global(span, default_ident.clone(), ~[]);
|
||||
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), ~[]);
|
||||
|
||||
return match *substr.fields {
|
||||
StaticStruct(_, ref summary) => {
|
||||
match *summary {
|
||||
Left(count) => {
|
||||
if count == 0 {
|
||||
Unnamed(ref fields) => {
|
||||
if fields.is_empty() {
|
||||
cx.expr_ident(span, substr.type_ident)
|
||||
} else {
|
||||
let exprs = vec::from_elem(count, default_call);
|
||||
let exprs = fields.map(|sp| default_call(*sp));
|
||||
cx.expr_call_ident(span, substr.type_ident, exprs)
|
||||
}
|
||||
}
|
||||
Right(ref fields) => {
|
||||
let default_fields = do fields.map |ident| {
|
||||
cx.field_imm(span, *ident, default_call)
|
||||
Named(ref fields) => {
|
||||
let default_fields = do fields.map |&(ident, span)| {
|
||||
cx.field_imm(span, ident, default_call(span))
|
||||
};
|
||||
cx.expr_struct_ident(span, substr.type_ident, default_fields)
|
||||
}
|
||||
|
|
|
@ -123,11 +123,11 @@ fn encodable_substructure(cx: @ExtCtxt, span: Span,
|
|||
let emit_struct_field = cx.ident_of("emit_struct_field");
|
||||
let mut stmts = ~[];
|
||||
for (i, f) in fields.iter().enumerate() {
|
||||
let (name, val) = match *f {
|
||||
(Some(id), e, _) => (cx.str_of(id), e),
|
||||
(None, e, _) => (format!("_field{}", i).to_managed(), e)
|
||||
let name = match f.name {
|
||||
Some(id) => cx.str_of(id),
|
||||
None => format!("_field{}", i).to_managed()
|
||||
};
|
||||
let enc = cx.expr_method_call(span, val, encode, ~[blkencoder]);
|
||||
let enc = cx.expr_method_call(span, f.self_, encode, ~[blkencoder]);
|
||||
let lambda = cx.lambda_expr_1(span, enc, blkarg);
|
||||
let call = cx.expr_method_call(span, blkencoder,
|
||||
emit_struct_field,
|
||||
|
@ -154,8 +154,7 @@ fn encodable_substructure(cx: @ExtCtxt, span: Span,
|
|||
let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
|
||||
let mut stmts = ~[];
|
||||
for (i, f) in fields.iter().enumerate() {
|
||||
let val = match *f { (_, e, _) => e };
|
||||
let enc = cx.expr_method_call(span, val, encode, ~[blkencoder]);
|
||||
let enc = cx.expr_method_call(span, f.self_, encode, ~[blkencoder]);
|
||||
let lambda = cx.lambda_expr_1(span, enc, blkarg);
|
||||
let call = cx.expr_method_call(span, blkencoder,
|
||||
emit_variant_arg,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -81,8 +81,8 @@ fn iter_bytes_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @
|
|||
_ => cx.span_bug(span, "Impossible substructure in `deriving(IterBytes)`")
|
||||
}
|
||||
|
||||
for &(_, field, _) in fields.iter() {
|
||||
exprs.push(call_iterbytes(field));
|
||||
for &FieldInfo { self_, _ } in fields.iter() {
|
||||
exprs.push(call_iterbytes(self_));
|
||||
}
|
||||
|
||||
if exprs.len() == 0 {
|
||||
|
|
|
@ -15,8 +15,6 @@ use ext::base::ExtCtxt;
|
|||
use ext::build::{AstBuilder};
|
||||
use ext::deriving::generic::*;
|
||||
|
||||
use std::vec;
|
||||
|
||||
pub fn expand_deriving_rand(cx: @ExtCtxt,
|
||||
span: Span,
|
||||
mitem: @MetaItem,
|
||||
|
@ -59,7 +57,7 @@ fn rand_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Expr {
|
|||
cx.ident_of("Rand"),
|
||||
cx.ident_of("rand")
|
||||
];
|
||||
let rand_call = || {
|
||||
let rand_call = |span| {
|
||||
cx.expr_call_global(span,
|
||||
rand_ident.clone(),
|
||||
~[ rng[0] ])
|
||||
|
@ -112,7 +110,7 @@ fn rand_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Expr {
|
|||
(ident, ref summary) => {
|
||||
cx.arm(span,
|
||||
~[ pat ],
|
||||
rand_thing(cx, span, ident, summary, || rand_call()))
|
||||
rand_thing(cx, span, ident, summary, |sp| rand_call(sp)))
|
||||
}
|
||||
}
|
||||
}.collect::<~[ast::Arm]>();
|
||||
|
@ -130,20 +128,20 @@ fn rand_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Expr {
|
|||
|
||||
fn rand_thing(cx: @ExtCtxt, span: Span,
|
||||
ctor_ident: Ident,
|
||||
summary: &Either<uint, ~[Ident]>,
|
||||
rand_call: &fn() -> @Expr) -> @Expr {
|
||||
summary: &StaticFields,
|
||||
rand_call: &fn(Span) -> @Expr) -> @Expr {
|
||||
match *summary {
|
||||
Left(count) => {
|
||||
if count == 0 {
|
||||
Unnamed(ref fields) => {
|
||||
if fields.is_empty() {
|
||||
cx.expr_ident(span, ctor_ident)
|
||||
} else {
|
||||
let exprs = vec::from_fn(count, |_| rand_call());
|
||||
let exprs = fields.map(|span| rand_call(*span));
|
||||
cx.expr_call_ident(span, ctor_ident, exprs)
|
||||
}
|
||||
}
|
||||
Right(ref fields) => {
|
||||
let rand_fields = do fields.map |ident| {
|
||||
cx.field_imm(span, *ident, rand_call())
|
||||
Named(ref fields) => {
|
||||
let rand_fields = do fields.map |&(ident, span)| {
|
||||
cx.field_imm(span, ident, rand_call(span))
|
||||
};
|
||||
cx.expr_struct_ident(span, ctor_ident, rand_fields)
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ fn to_str_substructure(cx: @ExtCtxt, span: Span,
|
|||
let to_str = cx.ident_of("to_str");
|
||||
|
||||
let doit = |start: &str, end: @str, name: ast::Ident,
|
||||
fields: &[(Option<ast::Ident>, @Expr, ~[@Expr])]| {
|
||||
fields: &[FieldInfo]| {
|
||||
if fields.len() == 0 {
|
||||
cx.expr_str_uniq(span, cx.str_of(name))
|
||||
} else {
|
||||
|
@ -65,7 +65,7 @@ fn to_str_substructure(cx: @ExtCtxt, span: Span,
|
|||
stmts.push(cx.stmt_expr(call));
|
||||
};
|
||||
|
||||
for (i, &(name, e, _)) in fields.iter().enumerate() {
|
||||
for (i, &FieldInfo {name, span, self_, _}) in fields.iter().enumerate() {
|
||||
if i > 0 {
|
||||
push(cx.expr_str(span, @", "));
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ fn to_str_substructure(cx: @ExtCtxt, span: Span,
|
|||
push(cx.expr_str(span, name.to_managed()));
|
||||
}
|
||||
}
|
||||
push(cx.expr_method_call(span, e, to_str, ~[]));
|
||||
push(cx.expr_method_call(span, self_, to_str, ~[]));
|
||||
}
|
||||
push(cx.expr_str(span, end));
|
||||
|
||||
|
@ -86,7 +86,7 @@ fn to_str_substructure(cx: @ExtCtxt, span: Span,
|
|||
|
||||
return match *substr.fields {
|
||||
Struct(ref fields) => {
|
||||
if fields.len() == 0 || fields[0].n0_ref().is_none() {
|
||||
if fields.len() == 0 || fields[0].name.is_none() {
|
||||
doit("(", @")", substr.type_ident, *fields)
|
||||
} else {
|
||||
doit("{", @"}", substr.type_ident, *fields)
|
||||
|
|
|
@ -14,8 +14,6 @@ use ext::base::ExtCtxt;
|
|||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
|
||||
use std::vec;
|
||||
|
||||
pub fn expand_deriving_zero(cx: @ExtCtxt,
|
||||
span: Span,
|
||||
mitem: @MetaItem,
|
||||
|
@ -62,22 +60,22 @@ fn zero_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Expr {
|
|||
cx.ident_of("Zero"),
|
||||
cx.ident_of("zero")
|
||||
];
|
||||
let zero_call = cx.expr_call_global(span, zero_ident.clone(), ~[]);
|
||||
let zero_call = |span| cx.expr_call_global(span, zero_ident.clone(), ~[]);
|
||||
|
||||
return match *substr.fields {
|
||||
StaticStruct(_, ref summary) => {
|
||||
match *summary {
|
||||
Left(count) => {
|
||||
if count == 0 {
|
||||
Unnamed(ref fields) => {
|
||||
if fields.is_empty() {
|
||||
cx.expr_ident(span, substr.type_ident)
|
||||
} else {
|
||||
let exprs = vec::from_elem(count, zero_call);
|
||||
let exprs = fields.map(|sp| zero_call(*sp));
|
||||
cx.expr_call_ident(span, substr.type_ident, exprs)
|
||||
}
|
||||
}
|
||||
Right(ref fields) => {
|
||||
let zero_fields = do fields.map |ident| {
|
||||
cx.field_imm(span, *ident, zero_call)
|
||||
Named(ref fields) => {
|
||||
let zero_fields = do fields.map |&(ident, span)| {
|
||||
cx.field_imm(span, ident, zero_call(span))
|
||||
};
|
||||
cx.expr_struct_ident(span, substr.type_ident, zero_fields)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
#[feature(struct_variant)];
|
||||
|
||||
struct NotEq;
|
||||
|
||||
#[deriving(Eq)]
|
||||
enum Foo {
|
||||
Bar {
|
||||
x: NotEq //~ ERROR mismatched types
|
||||
//~^ ERROR failed to find an implementation of trait std::cmp::Eq for NotEq
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {}
|
22
src/test/compile-fail/deriving-field-span-enum.rs
Normal file
22
src/test/compile-fail/deriving-field-span-enum.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
#[feature(struct_variant)];
|
||||
|
||||
struct NotEq;
|
||||
|
||||
#[deriving(Eq)]
|
||||
enum Foo {
|
||||
Bar(NotEq), //~ ERROR mismatched types
|
||||
//~^ ERROR failed to find an implementation of trait std::cmp::Eq for NotEq
|
||||
Baz { x: NotEq }
|
||||
}
|
||||
|
||||
pub fn main() {}
|
19
src/test/compile-fail/deriving-field-span-struct.rs
Normal file
19
src/test/compile-fail/deriving-field-span-struct.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
struct NotEq;
|
||||
|
||||
#[deriving(Eq)]
|
||||
struct Foo {
|
||||
x: NotEq //~ ERROR mismatched types
|
||||
//~^ ERROR failed to find an implementation of trait std::cmp::Eq for NotEq
|
||||
}
|
||||
|
||||
pub fn main() {}
|
19
src/test/compile-fail/deriving-field-span-tuple-struct.rs
Normal file
19
src/test/compile-fail/deriving-field-span-tuple-struct.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
struct NotEq;
|
||||
|
||||
#[deriving(Eq)]
|
||||
struct Foo (
|
||||
NotEq //~ ERROR mismatched types
|
||||
//~^ ERROR failed to find an implementation of trait std::cmp::Eq for NotEq
|
||||
);
|
||||
|
||||
pub fn main() {}
|
Loading…
Add table
Reference in a new issue