Skip to content

Commit 5b4d758

Browse files
committed
feat(syntex): Squash #[derive_X]#[derive_Y] into #[derive(X,Y)]
This avoids rust-lang/rust#32655.
1 parent ed1a9ad commit 5b4d758

File tree

3 files changed

+223
-1
lines changed

3 files changed

+223
-1
lines changed

syntex/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "syntex"
3-
version = "0.30.0"
3+
version = "0.30.1"
44
authors = [ "[email protected]" ]
55
license = "MIT/Apache-2.0"
66
description = "A library that enables compile time syntax extension expansion"

syntex/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
extern crate syntex_syntax;
22

3+
mod squash_derive;
4+
35
use std::fs::File;
46
use std::io::{self, Write};
57
use std::path::Path;
@@ -147,6 +149,7 @@ impl Registry {
147149
let ecx = ExtCtxt::new(&sess, cfg, ecfg, &mut gated_cfgs);
148150

149151
let (krate, _) = expand::expand_crate(ecx, self.macros, self.syntax_exts, krate);
152+
let krate = squash_derive::squash_derive(krate);
150153

151154
let krate = self.post_expansion_passes.iter()
152155
.fold(krate, |krate, f| (f)(krate));

syntex/src/squash_derive.rs

+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/// This crate exposes a simple folder that squashes:
2+
///
3+
/// ```rust,ignore
4+
/// #[derive_Foo]
5+
/// #[derive_Bar]
6+
/// struct Baz;
7+
/// ```
8+
///
9+
/// Into:
10+
///
11+
/// ```rust,ignore
12+
/// #[derive(Foo, Bar)]
13+
/// struct Baz;
14+
/// ```
15+
16+
use syntex_syntax::ast;
17+
use syntex_syntax::codemap::{Span, Spanned};
18+
use syntex_syntax::fold::{self, Folder};
19+
use syntex_syntax::parse::token;
20+
use syntex_syntax::ptr::P;
21+
use syntex_syntax::util::move_map::MoveMap;
22+
23+
/// Squash all the `#[derive_*]` into `#[derive(*)]` together.
24+
pub fn squash_derive(krate: ast::Crate) -> ast::Crate {
25+
SquashDeriveFolder.fold_crate(krate)
26+
}
27+
28+
struct SquashDeriveFolder;
29+
30+
impl Folder for SquashDeriveFolder {
31+
fn fold_item_simple(&mut self, mut item: ast::Item) -> ast::Item {
32+
let mut attr_folder = SquashDeriveAttrFolder { derive_attr: None };
33+
item.attrs = item.attrs.move_flat_map(|x| attr_folder.fold_attribute(x));
34+
35+
if let Some(derive_attr) = attr_folder.into_attr() {
36+
item.attrs.push(derive_attr);
37+
}
38+
39+
fold::noop_fold_item_simple(item, self)
40+
}
41+
42+
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
43+
fold::noop_fold_mac(mac, self)
44+
}
45+
}
46+
47+
struct SquashDeriveAttrFolder {
48+
derive_attr: Option<DeriveAttr>,
49+
}
50+
51+
impl SquashDeriveAttrFolder {
52+
fn into_attr(self) -> Option<ast::Attribute> {
53+
match self.derive_attr {
54+
Some(derive_attr) => {
55+
let meta_item = ast::MetaItemKind::List(
56+
token::intern_and_get_ident("derive"),
57+
derive_attr.meta_items,
58+
);
59+
60+
let meta_item = Spanned {
61+
node: meta_item,
62+
span: derive_attr.span,
63+
};
64+
65+
Some(Spanned {
66+
node: ast::Attribute_ {
67+
id: derive_attr.id,
68+
style: derive_attr.style,
69+
value: P(meta_item),
70+
is_sugared_doc: derive_attr.is_sugared_doc,
71+
},
72+
span: derive_attr.span,
73+
})
74+
}
75+
None => None,
76+
}
77+
}
78+
}
79+
80+
struct DeriveAttr {
81+
id: ast::AttrId,
82+
style: ast::AttrStyle,
83+
is_sugared_doc: bool,
84+
meta_items: Vec<P<ast::MetaItem>>,
85+
span: Span,
86+
}
87+
88+
impl Folder for SquashDeriveAttrFolder {
89+
fn fold_attribute(&mut self,
90+
Spanned {
91+
node: ast::Attribute_ { id, style, value, is_sugared_doc },
92+
span,
93+
}: ast::Attribute) -> Option<ast::Attribute> {
94+
match value.node {
95+
ast::MetaItemKind::Word(ref name) if name.starts_with("derive_") => {
96+
let (_, derive_name) = name.split_at("derive_".len());
97+
let derive_name = token::intern_and_get_ident(derive_name);
98+
99+
let meta_word = P(Spanned {
100+
node: ast::MetaItemKind::Word(derive_name),
101+
span: value.span,
102+
});
103+
104+
match self.derive_attr {
105+
Some(ref mut derive_attr) => {
106+
derive_attr.meta_items.push(meta_word);
107+
}
108+
None => {
109+
self.derive_attr = Some(DeriveAttr {
110+
id: id,
111+
style: style,
112+
is_sugared_doc: is_sugared_doc,
113+
meta_items: vec![meta_word],
114+
span: value.span,
115+
});
116+
}
117+
}
118+
119+
return None;
120+
}
121+
_ => { }
122+
}
123+
124+
Some(Spanned {
125+
node: ast::Attribute_ {
126+
id: id,
127+
style: style,
128+
value: value,
129+
is_sugared_doc: is_sugared_doc,
130+
},
131+
span: span,
132+
})
133+
}
134+
}
135+
136+
#[cfg(test)]
137+
mod tests {
138+
use syntex_syntax::ast;
139+
use syntex_syntax::codemap::{DUMMY_SP, Spanned};
140+
use syntex_syntax::fold::Folder;
141+
use syntex_syntax::parse::token;
142+
use syntex_syntax::ptr::P;
143+
144+
fn mk_meta_word(name: &str) -> P<ast::MetaItem> {
145+
let name = token::intern_and_get_ident(name);
146+
147+
P(Spanned {
148+
node: ast::MetaItemKind::Word(name),
149+
span: DUMMY_SP,
150+
})
151+
}
152+
153+
fn mk_meta_list(name: &str,
154+
meta_items: Vec<P<ast::MetaItem>>) -> P<ast::MetaItem> {
155+
let name = token::intern_and_get_ident(name);
156+
157+
P(Spanned {
158+
node: ast::MetaItemKind::List(name, meta_items),
159+
span: DUMMY_SP,
160+
})
161+
}
162+
163+
fn mk_attr(meta_item: P<ast::MetaItem>) -> ast::Attribute {
164+
Spanned {
165+
node: ast::Attribute_ {
166+
id: ast::AttrId(0),
167+
style: ast::AttrStyle::Outer,
168+
value: meta_item,
169+
is_sugared_doc: false,
170+
},
171+
span: DUMMY_SP,
172+
}
173+
}
174+
175+
#[test]
176+
fn test_squash() {
177+
let variant_data = ast::VariantData::Unit(ast::DUMMY_NODE_ID);
178+
179+
let generics = ast::Generics {
180+
lifetimes: vec![],
181+
ty_params: P::empty(),
182+
where_clause: ast::WhereClause {
183+
id: ast::DUMMY_NODE_ID,
184+
predicates: vec![],
185+
},
186+
};
187+
188+
let item_kind = ast::ItemKind::Struct(variant_data, generics);
189+
190+
let item = ast::Item {
191+
id: ast::DUMMY_NODE_ID,
192+
ident: token::str_to_ident("Foo"),
193+
attrs: vec![
194+
mk_attr(mk_meta_word("derive_A")),
195+
mk_attr(mk_meta_word("derive_B")),
196+
],
197+
node: item_kind.clone(),
198+
vis: ast::Visibility::Inherited,
199+
span: DUMMY_SP,
200+
};
201+
202+
assert_eq!(
203+
super::SquashDeriveFolder.fold_item_simple(item.clone()),
204+
ast::Item {
205+
id: ast::DUMMY_NODE_ID,
206+
ident: token::str_to_ident("Foo"),
207+
attrs: vec![
208+
mk_attr(mk_meta_list(
209+
"derive",
210+
vec![mk_meta_word("A"), mk_meta_word("B")],
211+
)),
212+
],
213+
node: item_kind,
214+
vis: ast::Visibility::Inherited,
215+
span: DUMMY_SP,
216+
}
217+
);
218+
}
219+
}

0 commit comments

Comments
 (0)