diff --git a/src/libcore/default.rs b/src/libcore/default.rs index af65fcc5a779d..07e503d12ad79 100644 --- a/src/libcore/default.rs +++ b/src/libcore/default.rs @@ -9,6 +9,26 @@ // except according to those terms. //! The `Default` trait for types which may have meaningful default values +//! +//! # Deriving +//! +//! This trait is derivable on structs using `#[deriving(Default)]`. When +//! deriving this trait, every field is initialized to `Default::default()`. +//! This can be overridden with the `#[default="expr"]` attribute on the field +//! itself. For example: +//! +//! ```rust +//! # use std::default::Default; +//! #[deriving(Default,Eq,Show)] +//! struct Frob { +//! #[default="42"] +//! x: uint, +//! y: uint +//! } +//! +//! let frob: Frob = Default::default(); +//! assert_eq!(frob, Frob { x: 42, y: 0 }); +//! ``` /// A trait that types which have a useful default value should implement. pub trait Default { diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index 062a7418287e3..a77d9996d39e1 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -1080,6 +1080,9 @@ static other_attrs: &'static [&'static str] = &[ "test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start", "no_split_stack", "cold", "macro_registrar", "linkage", + // struct-field-level + "default", + // internal attribute: bypass privacy inside items "!resolve_unexported", ]; diff --git a/src/libsyntax/ext/deriving/default.rs b/src/libsyntax/ext/deriving/default.rs index c225906ed2bab..e2d08c7518f84 100644 --- a/src/libsyntax/ext/deriving/default.rs +++ b/src/libsyntax/ext/deriving/default.rs @@ -8,11 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use ast; use ast::{MetaItem, Item, Expr}; +use attr::{AttrMetaMethods, AttributeMethods}; use codemap::Span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; +use ext::quote::rt::ExtParseUtils; +use fold::Folder; use parse::token::InternedString; pub fn expand_deriving_default(cx: &mut ExtCtxt, @@ -52,24 +56,35 @@ fn default_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructur cx.ident_of("Default"), cx.ident_of("default") ); - let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new()); + let default_call = |cx: &mut ExtCtxt, span| cx.expr_call_global(span, default_ident.clone(), + Vec::new()); return match *substr.fields { - StaticStruct(_, ref summary) => { + StaticStruct(struct_def, ref summary) => { match *summary { Unnamed(ref fields) => { if fields.is_empty() { cx.expr_ident(trait_span, substr.type_ident) } else { - let exprs = fields.iter().map(|sp| default_call(*sp)).collect(); + let exprs = fields.iter().zip(struct_def.fields.iter()).map(|(sp, field)| { + match get_default_attr_expr(cx, field) { + None => default_call(cx, *sp), + Some(e) => e + } + }).collect(); cx.expr_call_ident(trait_span, substr.type_ident, exprs) } } Named(ref fields) => { - let default_fields = fields.iter().map(|&(ident, span)| { - cx.field_imm(span, ident, default_call(span)) + let exprs = fields.iter().zip(struct_def.fields.iter()) + .map(|(&(ident, span), field)| { + let expr = match get_default_attr_expr(cx, field) { + None => default_call(cx, span), + Some(e) => e + }; + cx.field_imm(span, ident, expr) }).collect(); - cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) + cx.expr_struct_ident(trait_span, substr.type_ident, exprs) } } } @@ -81,3 +96,41 @@ fn default_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructur _ => cx.span_bug(trait_span, "Non-static method in `deriving(Default)`") }; } + +fn get_default_attr_expr(cx: &mut ExtCtxt, field: &ast::StructField) -> Option<@Expr> { + let attrs = field.node.attrs.as_slice(); + attrs.iter().find(|at| at.name().get() == "default").and_then(|at| { + match at.meta().node { + ast::MetaNameValue(_, ref v) => { + match v.node { + ast::LitStr(ref s, _) => { + let s = s.get().to_strbuf(); + let expr = cx.parse_expr(s); + let mut folder = SpanFolder { sp: v.span }; + Some(folder.fold_expr(expr)) + + } + _ => { + cx.span_err(v.span, + "non-string literals are not allowed in `#[default]` attribute"); + None + } + } + } + _ => { + cx.span_err(at.span, "`#[default]` attribute must have a value"); + None + } + } + }) +} + +struct SpanFolder { + sp: Span +} + +impl Folder for SpanFolder { + fn new_span(&mut self, _sp: Span) -> Span { + self.sp + } +} diff --git a/src/test/compile-fail/deriving-Default-attributes-struct-expr.rs b/src/test/compile-fail/deriving-Default-attributes-struct-expr.rs new file mode 100644 index 0000000000000..68b6a95b28e2f --- /dev/null +++ b/src/test/compile-fail/deriving-Default-attributes-struct-expr.rs @@ -0,0 +1,17 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deriving(Default)] +struct StructBadExpr { + #[default="sdlkfj"] //~ ERROR unresolved name `sdlkfj` + x: int +} + +fn main() {} diff --git a/src/test/compile-fail/deriving-Default-attributes-struct.rs b/src/test/compile-fail/deriving-Default-attributes-struct.rs new file mode 100644 index 0000000000000..f797f0dd5468b --- /dev/null +++ b/src/test/compile-fail/deriving-Default-attributes-struct.rs @@ -0,0 +1,20 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deriving(Default)] +struct Struct { + #[default="3"] + x: int, + #[default] //~ ERROR `#[default]` attribute must have a value + y: int + // FIXME: add a test for #[default=3] once non-string literals parse in attributes +} + +fn main() {} diff --git a/src/test/compile-fail/deriving-Default-attributes-tuple-expr.rs b/src/test/compile-fail/deriving-Default-attributes-tuple-expr.rs new file mode 100644 index 0000000000000..3d5e4985ec0e4 --- /dev/null +++ b/src/test/compile-fail/deriving-Default-attributes-tuple-expr.rs @@ -0,0 +1,19 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deriving(Default)] +struct TupleBadExprs( + #[default="3"] //~ ERROR mismatched types: expected `()` + (), + #[default="42i"] //~ ERROR mismatched types: expected `uint` + uint +); + +fn main() {} diff --git a/src/test/compile-fail/deriving-Default-attributes-tuple.rs b/src/test/compile-fail/deriving-Default-attributes-tuple.rs new file mode 100644 index 0000000000000..d21f2773c4f98 --- /dev/null +++ b/src/test/compile-fail/deriving-Default-attributes-tuple.rs @@ -0,0 +1,20 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deriving(Default)] +struct Tuple( + #[default="3"] + int, + #[default] //~ ERROR `#[default]` attribute must have a value + int, + // FIXME: add a test for #[default=3] once non-string literals parse in attributes +); + +fn main() {} diff --git a/src/test/run-pass/deriving-Default-attributes.rs b/src/test/run-pass/deriving-Default-attributes.rs new file mode 100644 index 0000000000000..c92ccdfea9bcb --- /dev/null +++ b/src/test/run-pass/deriving-Default-attributes.rs @@ -0,0 +1,84 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::default::Default; + +#[deriving(Default,Eq,Show)] +struct Struct { + #[default="3"] + x: int, + #[default=r#""test""#] + s: &'static str, + #[default="primes()"] + p: Vec, + #[default="factorial(5)"] + f: uint, + child: Child, + #[default=r#"format!("n: {}", 42)"#] + s2: ~str +} + +#[deriving(Default,Eq,Show)] +struct Tuple( + #[default="42"] + int, + #[default=r#""test2""#] + &'static str, + #[default="primes()"] + Vec, + #[default="factorial(5)"] + uint, + Child, + #[default=r#"format!("n: {}", 42)"#] + ~str +); + +#[deriving(Eq,Show)] +struct Child { + name: &'static str +} + +impl Default for Child { + fn default() -> Child { + Child { name: "child" } + } +} + +fn primes() -> Vec { + vec![2u, 3, 5, 7, 11, 13, 17, 23] +} + +fn factorial(n: uint) -> uint { + match n { + 0|1 => 1, + n => n*factorial(n-1) + } +} + +fn main() { + let s: Struct = Default::default(); + assert_eq!(s, Struct { + x: 3, + s: "test", + p: primes(), + f: 120, + child: Child { name: "child" }, + s2: "n: 42".to_owned() + }); + let t: Tuple = Default::default(); + assert_eq!(t, Tuple( + 42, + "test2", + primes(), + 120, + Child { name: "child" }, + "n: 42".to_owned() + )); +}