Skip to content

Commit db16606

Browse files
committed
Add a test for #[deriving(...)] working with annotations
1 parent 3142a12 commit db16606

File tree

2 files changed

+237
-0
lines changed

2 files changed

+237
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// force-host
12+
// no-prefer-dynamic
13+
14+
#![crate_type = "dylib"]
15+
#![feature(plugin_registrar, quote)]
16+
17+
extern crate syntax;
18+
extern crate rustc;
19+
20+
use std::gc::Gc;
21+
22+
use syntax::ast::{
23+
Expr,
24+
Ident,
25+
Item,
26+
MetaItem,
27+
};
28+
use syntax::ast;
29+
use syntax::attr;
30+
use syntax::codemap::Span;
31+
use syntax::ext::base::{ExtCtxt, ItemDecorator};
32+
use syntax::ext::build::AstBuilder;
33+
use syntax::ext::deriving::generic::{
34+
MethodDef,
35+
Named,
36+
StaticStruct,
37+
Struct,
38+
Substructure,
39+
TraitDef,
40+
Unnamed,
41+
combine_substructure,
42+
};
43+
use syntax::ext::deriving::generic::ty::{
44+
Borrowed,
45+
LifetimeBounds,
46+
Literal,
47+
Self,
48+
Path,
49+
Ptr,
50+
borrowed_explicit_self,
51+
};
52+
use syntax::parse::token;
53+
54+
use rustc::plugin::Registry;
55+
56+
#[plugin_registrar]
57+
pub fn registrar(reg: &mut Registry) {
58+
reg.register_syntax_extension(
59+
token::intern("deriving_field_names"),
60+
ItemDecorator(expand_deriving_field_names));
61+
}
62+
63+
fn expand_deriving_field_names(cx: &mut ExtCtxt,
64+
sp: Span,
65+
mitem: Gc<MetaItem>,
66+
item: Gc<Item>,
67+
push: |Gc<ast::Item>|) {
68+
let trait_def = TraitDef {
69+
span: sp,
70+
attributes: vec!(),
71+
path: Path::new_local("FieldNames"),
72+
additional_bounds: Vec::new(),
73+
generics: LifetimeBounds::empty(),
74+
methods: vec!(
75+
MethodDef {
76+
name: "field_names",
77+
generics: LifetimeBounds::empty(),
78+
explicit_self: borrowed_explicit_self(),
79+
args: Vec::new(),
80+
ret_ty: Literal(
81+
Path::new_(
82+
vec!("Vec"),
83+
None,
84+
vec!(
85+
box Ptr(
86+
box Literal(Path::new_local("str")),
87+
Borrowed(Some("'static"), ast::MutImmutable))),
88+
false)),
89+
attributes: vec!(),
90+
combine_substructure: combine_substructure(|a, b, c| {
91+
field_names_substructure(a, b, c)
92+
}),
93+
},
94+
MethodDef {
95+
name: "static_field_names",
96+
generics: LifetimeBounds::empty(),
97+
explicit_self: None,
98+
args: vec!(
99+
Literal(
100+
Path::new_(
101+
vec!("std", "option", "Option"),
102+
None,
103+
vec!(box Self),
104+
true))),
105+
ret_ty: Literal(
106+
Path::new_(
107+
vec!("Vec"),
108+
None,
109+
vec!(
110+
box Ptr(
111+
box Literal(Path::new_local("str")),
112+
Borrowed(Some("'static"), ast::MutImmutable))),
113+
false)),
114+
attributes: vec!(),
115+
combine_substructure: combine_substructure(|a, b, c| {
116+
field_names_substructure(a, b, c)
117+
}),
118+
},
119+
)
120+
};
121+
122+
trait_def.expand(cx, mitem, item, push)
123+
}
124+
125+
fn field_names_substructure(cx: &ExtCtxt,
126+
_span: Span,
127+
substr: &Substructure) -> Gc<Expr> {
128+
129+
let ident_attrs: Vec<(Span, Option<Ident>, &[ast::Attribute])> = match substr.fields {
130+
Struct(ref fields) => {
131+
fields.iter()
132+
.map(|field_info| {
133+
(field_info.span, field_info.name, field_info.attrs)
134+
})
135+
.collect()
136+
}
137+
StaticStruct(_, ref summary) => {
138+
match *summary {
139+
Unnamed(_) => {
140+
fail!()
141+
}
142+
Named(ref fields) => {
143+
fields.iter()
144+
.map(|field_info| {
145+
(field_info.span, Some(field_info.name), field_info.attrs)
146+
})
147+
.collect()
148+
}
149+
}
150+
}
151+
152+
_ => cx.bug("expected Struct in deriving_test")
153+
};
154+
155+
let stmts: Vec<Gc<ast::Stmt>> = ident_attrs.iter()
156+
.enumerate()
157+
.map(|(i, &(span, ident, attrs))| {
158+
let name = find_name(attrs);
159+
160+
let name = match (name, ident) {
161+
(Some(serial), _) => serial.clone(),
162+
(None, Some(id)) => token::get_ident(id),
163+
(None, None) => token::intern_and_get_ident(format!("_field{}", i).as_slice()),
164+
};
165+
166+
let name = cx.expr_str(span, name);
167+
168+
quote_stmt!(
169+
cx,
170+
parts.push($name);
171+
)
172+
})
173+
.collect();
174+
175+
quote_expr!(cx, {
176+
let mut parts = Vec::new();
177+
$stmts
178+
parts
179+
})
180+
}
181+
182+
fn find_name(attrs: &[ast::Attribute]) -> Option<token::InternedString> {
183+
for attr in attrs.iter() {
184+
match attr.node.value.node {
185+
ast::MetaNameValue(ref at_name, ref value) => {
186+
match (at_name.get(), &value.node) {
187+
("name", &ast::LitStr(ref string, _)) => {
188+
attr::mark_used(attr);
189+
return Some(string.clone());
190+
},
191+
_ => ()
192+
}
193+
},
194+
_ => ()
195+
}
196+
}
197+
None
198+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:syntax-extension-with-field-names.rs
12+
// ignore-stage1
13+
14+
#![feature(phase)]
15+
16+
#[phase(plugin)]
17+
extern crate "syntax-extension-with-field-names" as extension;
18+
19+
trait FieldNames {
20+
fn field_names(&self) -> Vec<&'static str>;
21+
fn static_field_names(_: Option<Self>) -> Vec<&'static str>;
22+
}
23+
24+
#[deriving_field_names]
25+
struct Foo {
26+
x: int,
27+
#[name = "$y"]
28+
y: int,
29+
}
30+
31+
fn main() {
32+
let foo = Foo {
33+
x: 1,
34+
y: 2,
35+
};
36+
37+
assert_eq!(foo.field_names(), vec!("x", "$y"));
38+
assert_eq!(FieldNames::static_field_names(None::<Foo>), vec!("x", "$y"));
39+
}

0 commit comments

Comments
 (0)