Skip to content
This repository has been archived by the owner on May 6, 2020. It is now read-only.

feat(syntex): Squash #[derive_X]#[derive_Y] into #[derive(X,Y)] #39

Merged
merged 1 commit into from
Apr 4, 2016
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
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ rust:
- nightly
script:
- (cd hello_world && cargo build --verbose)
- (cd syntex && cargo doc --no-deps)
- (cd syntex && cargo test --verbose && cargo doc --no-deps)
- (cd syntex_syntax && cargo doc --no-deps)
after_success: |
[ $TRAVIS_BRANCH = "master" ] &&
Expand Down
2 changes: 1 addition & 1 deletion syntex/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "syntex"
version = "0.30.0"
version = "0.30.1"
authors = [ "erick.tryzelaar@gmail.com" ]
license = "MIT/Apache-2.0"
description = "A library that enables compile time syntax extension expansion"
Expand Down
3 changes: 3 additions & 0 deletions syntex/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
extern crate syntex_syntax;

mod squash_derive;

use std::fs::File;
use std::io::{self, Write};
use std::path::Path;
Expand Down Expand Up @@ -147,6 +149,7 @@ impl Registry {
let ecx = ExtCtxt::new(&sess, cfg, ecfg, &mut gated_cfgs);

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

let krate = self.post_expansion_passes.iter()
.fold(krate, |krate, f| (f)(krate));
Expand Down
219 changes: 219 additions & 0 deletions syntex/src/squash_derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/// This crate exposes a simple folder that squashes:
///
/// ```rust,ignore
/// #[derive_Foo]
/// #[derive_Bar]
/// struct Baz;
/// ```
///
/// Into:
///
/// ```rust,ignore
/// #[derive(Foo, Bar)]
/// struct Baz;
/// ```

use syntex_syntax::ast;
use syntex_syntax::codemap::{Span, Spanned};
use syntex_syntax::fold::{self, Folder};
use syntex_syntax::parse::token;
use syntex_syntax::ptr::P;
use syntex_syntax::util::move_map::MoveMap;

/// Squash all the `#[derive_*]` into `#[derive(*)]` together.
pub fn squash_derive(krate: ast::Crate) -> ast::Crate {
SquashDeriveFolder.fold_crate(krate)
}

struct SquashDeriveFolder;

impl Folder for SquashDeriveFolder {
fn fold_item_simple(&mut self, mut item: ast::Item) -> ast::Item {
let mut attr_folder = SquashDeriveAttrFolder { derive_attr: None };
item.attrs = item.attrs.move_flat_map(|x| attr_folder.fold_attribute(x));

if let Some(derive_attr) = attr_folder.into_attr() {
item.attrs.push(derive_attr);
}

fold::noop_fold_item_simple(item, self)
}

fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
fold::noop_fold_mac(mac, self)
}
}

struct SquashDeriveAttrFolder {
derive_attr: Option<DeriveAttr>,
}

impl SquashDeriveAttrFolder {
fn into_attr(self) -> Option<ast::Attribute> {
match self.derive_attr {
Some(derive_attr) => {
let meta_item = ast::MetaItemKind::List(
token::intern_and_get_ident("derive"),
derive_attr.meta_items,
);

let meta_item = Spanned {
node: meta_item,
span: derive_attr.span,
};

Some(Spanned {
node: ast::Attribute_ {
id: derive_attr.id,
style: derive_attr.style,
value: P(meta_item),
is_sugared_doc: derive_attr.is_sugared_doc,
},
span: derive_attr.span,
})
}
None => None,
}
}
}

struct DeriveAttr {
id: ast::AttrId,
style: ast::AttrStyle,
is_sugared_doc: bool,
meta_items: Vec<P<ast::MetaItem>>,
span: Span,
}

impl Folder for SquashDeriveAttrFolder {
fn fold_attribute(&mut self,
Spanned {
node: ast::Attribute_ { id, style, value, is_sugared_doc },
span,
}: ast::Attribute) -> Option<ast::Attribute> {
match value.node {
ast::MetaItemKind::Word(ref name) if name.starts_with("derive_") => {
let (_, derive_name) = name.split_at("derive_".len());
let derive_name = token::intern_and_get_ident(derive_name);

let meta_word = P(Spanned {
node: ast::MetaItemKind::Word(derive_name),
span: value.span,
});

match self.derive_attr {
Some(ref mut derive_attr) => {
derive_attr.meta_items.push(meta_word);
}
None => {
self.derive_attr = Some(DeriveAttr {
id: id,
style: style,
is_sugared_doc: is_sugared_doc,
meta_items: vec![meta_word],
span: value.span,
});
}
}

return None;
}
_ => { }
}

Some(Spanned {
node: ast::Attribute_ {
id: id,
style: style,
value: value,
is_sugared_doc: is_sugared_doc,
},
span: span,
})
}
}

#[cfg(test)]
mod tests {
use syntex_syntax::ast;
use syntex_syntax::codemap::{DUMMY_SP, Spanned};
use syntex_syntax::fold::Folder;
use syntex_syntax::parse::token;
use syntex_syntax::ptr::P;

fn mk_meta_word(name: &str) -> P<ast::MetaItem> {
let name = token::intern_and_get_ident(name);

P(Spanned {
node: ast::MetaItemKind::Word(name),
span: DUMMY_SP,
})
}

fn mk_meta_list(name: &str,
meta_items: Vec<P<ast::MetaItem>>) -> P<ast::MetaItem> {
let name = token::intern_and_get_ident(name);

P(Spanned {
node: ast::MetaItemKind::List(name, meta_items),
span: DUMMY_SP,
})
}

fn mk_attr(meta_item: P<ast::MetaItem>) -> ast::Attribute {
Spanned {
node: ast::Attribute_ {
id: ast::AttrId(0),
style: ast::AttrStyle::Outer,
value: meta_item,
is_sugared_doc: false,
},
span: DUMMY_SP,
}
}

#[test]
fn test_squash() {
let variant_data = ast::VariantData::Unit(ast::DUMMY_NODE_ID);

let generics = ast::Generics {
lifetimes: vec![],
ty_params: P::empty(),
where_clause: ast::WhereClause {
id: ast::DUMMY_NODE_ID,
predicates: vec![],
},
};

let item_kind = ast::ItemKind::Struct(variant_data, generics);

let item = ast::Item {
id: ast::DUMMY_NODE_ID,
ident: token::str_to_ident("Foo"),
attrs: vec![
mk_attr(mk_meta_word("derive_A")),
mk_attr(mk_meta_word("derive_B")),
],
node: item_kind.clone(),
vis: ast::Visibility::Inherited,
span: DUMMY_SP,
};

assert_eq!(
super::SquashDeriveFolder.fold_item_simple(item.clone()),
ast::Item {
id: ast::DUMMY_NODE_ID,
ident: token::str_to_ident("Foo"),
attrs: vec![
mk_attr(mk_meta_list(
"derive",
vec![mk_meta_word("A"), mk_meta_word("B")],
)),
],
node: item_kind,
vis: ast::Visibility::Inherited,
span: DUMMY_SP,
}
);
}
}