Skip to content

Improve the spans of some deriving traits #11834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 27, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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