Skip to content

Commit

Permalink
auto merge of #11834 : huonw/rust/deriving-spans, r=alexcrichton
Browse files Browse the repository at this point in the history
I'd forgotten to update them when I changed this a while ago; it now displays error messages linked to the struct/variant field, rather than the `#[deriving(Trait)]` line, for all traits.

This also adds a very large number of autogenerated tests. I can easily remove/tone down that commit if necessary.
  • Loading branch information
bors committed Jan 27, 2014
2 parents 4b2fdfa + d9a204b commit b0280ac
Show file tree
Hide file tree
Showing 42 changed files with 1,051 additions and 165 deletions.
131 changes: 131 additions & 0 deletions src/etc/generate-deriving-span-tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/bin/env python
# xfail-license
# 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.

"""
This script creates a pile of compile-fail tests check that all the
derivings have spans that point to the fields, rather than the
#[deriving(...)] line.
sample usage: src/etc/generate-deriving-span-tests.py
"""

import sys, os, datetime, stat

TEST_DIR = os.path.abspath(
os.path.join(os.path.dirname(__file__), '../test/compile-fail'))

YEAR = datetime.datetime.now().year

TEMPLATE = """// Copyright {year} 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.
// This file was auto-generated using 'src/etc/generate-keyword-span-tests.py'
#[feature(struct_variant)];
extern mod extra;
{error_deriving}
struct Error;
{code}
fn main() {{}}
"""

ENUM_STRING = """
#[deriving({traits})]
enum Enum {{
A(
Error {errors}
)
}}
"""
ENUM_STRUCT_VARIANT_STRING = """
#[deriving({traits})]
enum Enum {{
A {{
x: Error {errors}
}}
}}
"""
STRUCT_STRING = """
#[deriving({traits})]
struct Struct {{
x: Error {errors}
}}
"""
STRUCT_TUPLE_STRING = """
#[deriving({traits})]
struct Struct(
Error {errors}
);
"""

ENUM_TUPLE, ENUM_STRUCT, STRUCT_FIELDS, STRUCT_TUPLE = range(4)

def create_test_case(type, trait, super_traits, number_of_errors):
string = [ENUM_STRING, ENUM_STRUCT_VARIANT_STRING, STRUCT_STRING, STRUCT_TUPLE_STRING][type]
all_traits = ','.join([trait] + super_traits)
super_traits = ','.join(super_traits)
error_deriving = '#[deriving(%s)]' % super_traits if super_traits else ''

errors = '\n'.join('//~%s ERROR' % ('^' * n) for n in range(error_count))
code = string.format(traits = all_traits, errors = errors)
return TEMPLATE.format(year = YEAR, error_deriving=error_deriving, code = code)

def write_file(name, string):
test_file = os.path.join(TEST_DIR, 'deriving-span-%s.rs' % name)

# set write permission if file exists, so it can be changed
if os.path.exists(test_file):
os.chmod(test_file, stat.S_IWUSR)

with open(test_file, 'wt') as f:
f.write(string)

# mark file read-only
os.chmod(test_file, stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH)



ENUM = 1
STRUCT = 2
ALL = STRUCT | ENUM

traits = {
'Zero': (STRUCT, [], 1),
'Default': (STRUCT, [], 1),
'FromPrimitive': (0, [], 0), # only works for C-like enums

'Decodable': (0, [], 0), # FIXME: quoting gives horrible spans
'Encodable': (0, [], 0), # FIXME: quoting gives horrible spans
}

for (trait, supers, errs) in [('Rand', [], 1),
('Clone', [], 1), ('DeepClone', ['Clone'], 1),
('Eq', [], 2), ('Ord', [], 8),
('TotalEq', [], 2), ('TotalOrd', ['TotalEq'], 2)]:
traits[trait] = (ALL, supers, errs)

for (trait, (types, super_traits, error_count)) in traits.items():
mk = lambda ty: create_test_case(ty, trait, super_traits, error_count)
if types & ENUM:
write_file(trait + '-enum', mk(ENUM_TUPLE))
write_file(trait + '-enum-struct-variant', mk(ENUM_STRUCT))
if types & STRUCT:
write_file(trait + '-struct', mk(STRUCT_FIELDS))
write_file(trait + '-tuple-struct', mk(STRUCT_TUPLE))
32 changes: 16 additions & 16 deletions src/libsyntax/ext/deriving/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ pub fn expand_deriving_deep_clone(cx: &ExtCtxt,

fn cs_clone(
name: &str,
cx: &ExtCtxt, span: Span,
cx: &ExtCtxt, trait_span: Span,
substr: &Substructure) -> @Expr {
let clone_ident = substr.method_ident;
let ctor_ident;
let all_fields;
let subcall = |field|
cx.expr_method_call(span, field, clone_ident, ~[]);
let subcall = |field: &FieldInfo|
cx.expr_method_call(field.span, field.self_, clone_ident, ~[]);

match *substr.fields {
Struct(ref af) => {
Expand All @@ -91,37 +91,37 @@ fn cs_clone(
ctor_ident = variant.node.name;
all_fields = af;
},
EnumNonMatching(..) => cx.span_bug(span,
format!("Non-matching enum variants in `deriving({})`",
name)),
StaticEnum(..) | StaticStruct(..) => cx.span_bug(span,
format!("Static method in `deriving({})`",
name))
EnumNonMatching(..) => cx.span_bug(trait_span,
format!("Non-matching enum variants in `deriving({})`",
name)),
StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span,
format!("Static method in `deriving({})`",
name))
}

match *all_fields {
[FieldInfo { name: None, .. }, ..] => {
// enum-like
let subcalls = all_fields.map(|field| subcall(field.self_));
cx.expr_call_ident(span, ctor_ident, subcalls)
let subcalls = all_fields.map(subcall);
cx.expr_call_ident(trait_span, ctor_ident, subcalls)
},
_ => {
// struct-like
let fields = all_fields.map(|field| {
let ident = match field.name {
Some(i) => i,
None => cx.span_bug(span,
None => cx.span_bug(trait_span,
format!("unnamed field in normal struct in `deriving({})`",
name))
name))
};
cx.field_imm(span, ident, subcall(field.self_))
cx.field_imm(field.span, ident, subcall(field))
});

if fields.is_empty() {
// no fields, so construct like `None`
cx.expr_ident(span, ctor_ident)
cx.expr_ident(trait_span, ctor_ident)
} else {
cx.expr_struct_ident(span, ctor_ident, fields)
cx.expr_struct_ident(trait_span, ctor_ident, fields)
}
}
}
Expand Down
53 changes: 26 additions & 27 deletions src/libsyntax/ext/deriving/decodable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub fn expand_deriving_decodable(cx: &ExtCtxt,
trait_def.expand(mitem, in_items)
}

fn decodable_substructure(cx: &ExtCtxt, span: Span,
fn decodable_substructure(cx: &ExtCtxt, trait_span: Span,
substr: &Substructure) -> @Expr {
let decoder = substr.nonself_args[0];
let recurse = ~[cx.ident_of("extra"),
Expand All @@ -60,9 +60,9 @@ fn decodable_substructure(cx: &ExtCtxt, span: Span,
cx.ident_of("decode")];
// throw an underscore in front to suppress unused variable warnings
let blkarg = cx.ident_of("_d");
let blkdecoder = cx.expr_ident(span, blkarg);
let calldecode = cx.expr_call_global(span, recurse, ~[blkdecoder]);
let lambdadecode = cx.lambda_expr_1(span, calldecode, blkarg);
let blkdecoder = cx.expr_ident(trait_span, blkarg);
let calldecode = cx.expr_call_global(trait_span, recurse, ~[blkdecoder]);
let lambdadecode = cx.lambda_expr_1(trait_span, calldecode, blkarg);

return match *substr.fields {
StaticStruct(_, ref summary) => {
Expand All @@ -73,7 +73,7 @@ fn decodable_substructure(cx: &ExtCtxt, span: Span,
let read_struct_field = cx.ident_of("read_struct_field");

let result = decode_static_fields(cx,
span,
trait_span,
substr.type_ident,
summary,
|span, name, field| {
Expand All @@ -82,10 +82,10 @@ fn decodable_substructure(cx: &ExtCtxt, span: Span,
cx.expr_uint(span, field),
lambdadecode])
});
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),
cx.lambda_expr_1(span, result, blkarg)])
cx.expr_method_call(trait_span, decoder, cx.ident_of("read_struct"),
~[cx.expr_str(trait_span, cx.str_of(substr.type_ident)),
cx.expr_uint(trait_span, nfields),
cx.lambda_expr_1(trait_span, result, blkarg)])
}
StaticEnum(_, ref fields) => {
let variant = cx.ident_of("i");
Expand All @@ -94,12 +94,11 @@ fn decodable_substructure(cx: &ExtCtxt, span: Span,
let mut variants = ~[];
let rvariant_arg = cx.ident_of("read_enum_variant_arg");

for (i, f) in fields.iter().enumerate() {
let (name, parts) = match *f { (i, ref p) => (i, p) };
variants.push(cx.expr_str(span, cx.str_of(name)));
for (i, &(name, v_span, ref parts)) in fields.iter().enumerate() {
variants.push(cx.expr_str(v_span, cx.str_of(name)));

let decoded = decode_static_fields(cx,
span,
v_span,
name,
parts,
|span, _, field| {
Expand All @@ -108,22 +107,22 @@ fn decodable_substructure(cx: &ExtCtxt, span: Span,
lambdadecode])
});

arms.push(cx.arm(span,
~[cx.pat_lit(span, cx.expr_uint(span, i))],
arms.push(cx.arm(v_span,
~[cx.pat_lit(v_span, cx.expr_uint(v_span, i))],
decoded));
}

arms.push(cx.arm_unreachable(span));
arms.push(cx.arm_unreachable(trait_span));

let result = cx.expr_match(span, cx.expr_ident(span, variant), arms);
let lambda = cx.lambda_expr(span, ~[blkarg, variant], result);
let variant_vec = cx.expr_vec(span, variants);
let result = cx.expr_method_call(span, blkdecoder,
let result = cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms);
let lambda = cx.lambda_expr(trait_span, ~[blkarg, variant], result);
let variant_vec = cx.expr_vec(trait_span, variants);
let result = cx.expr_method_call(trait_span, blkdecoder,
cx.ident_of("read_enum_variant"),
~[variant_vec, lambda]);
cx.expr_method_call(span, decoder, cx.ident_of("read_enum"),
~[cx.expr_str(span, cx.str_of(substr.type_ident)),
cx.lambda_expr_1(span, result, blkarg)])
cx.expr_method_call(trait_span, decoder, cx.ident_of("read_enum"),
~[cx.expr_str(trait_span, cx.str_of(substr.type_ident)),
cx.lambda_expr_1(trait_span, result, blkarg)])
}
_ => cx.bug("expected StaticEnum or StaticStruct in deriving(Decodable)")
};
Expand All @@ -133,29 +132,29 @@ fn decodable_substructure(cx: &ExtCtxt, span: Span,
/// - `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,
trait_span: Span,
outer_pat_ident: Ident,
fields: &StaticFields,
getarg: |Span, @str, uint| -> @Expr)
-> @Expr {
match *fields {
Unnamed(ref fields) => {
if fields.is_empty() {
cx.expr_ident(outer_span, outer_pat_ident)
cx.expr_ident(trait_span, outer_pat_ident)
} else {
let fields = 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)
cx.expr_call_ident(trait_span, outer_pat_ident, fields)
}
}
Named(ref fields) => {
// use the field's span to get nicer error messages.
let fields = 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)
cx.expr_struct_ident(trait_span, outer_pat_ident, fields)
}
}
}
14 changes: 7 additions & 7 deletions src/libsyntax/ext/deriving/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn expand_deriving_default(cx: &ExtCtxt,
trait_def.expand(mitem, in_items)
}

fn default_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) -> @Expr {
fn default_substructure(cx: &ExtCtxt, trait_span: Span, substr: &Substructure) -> @Expr {
let default_ident = ~[
cx.ident_of("std"),
cx.ident_of("default"),
Expand All @@ -55,25 +55,25 @@ fn default_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) -> @Exp
match *summary {
Unnamed(ref fields) => {
if fields.is_empty() {
cx.expr_ident(span, substr.type_ident)
cx.expr_ident(trait_span, substr.type_ident)
} else {
let exprs = fields.map(|sp| default_call(*sp));
cx.expr_call_ident(span, substr.type_ident, exprs)
cx.expr_call_ident(trait_span, substr.type_ident, exprs)
}
}
Named(ref fields) => {
let default_fields = fields.map(|&(ident, span)| {
cx.field_imm(span, ident, default_call(span))
});
cx.expr_struct_ident(span, substr.type_ident, default_fields)
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
}
}
}
StaticEnum(..) => {
cx.span_err(span, "`Default` cannot be derived for enums, only structs");
cx.span_err(trait_span, "`Default` cannot be derived for enums, only structs");
// let compilation continue
cx.expr_uint(span, 0)
cx.expr_uint(trait_span, 0)
}
_ => cx.bug("Non-static method in `deriving(Default)`")
_ => cx.span_bug(trait_span, "Non-static method in `deriving(Default)`")
};
}
Loading

0 comments on commit b0280ac

Please sign in to comment.