From 5b4d75829df7df7d9d0f4ef1f047d24aaae8d7db Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 3 Apr 2016 20:52:17 -0700 Subject: [PATCH] feat(syntex): Squash `#[derive_X]#[derive_Y]` into `#[derive(X,Y)]` This avoids https://github.com/rust-lang/rust/issues/32655. --- syntex/Cargo.toml | 2 +- syntex/src/lib.rs | 3 + syntex/src/squash_derive.rs | 219 ++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 syntex/src/squash_derive.rs diff --git a/syntex/Cargo.toml b/syntex/Cargo.toml index cbf05210..1dd30aa3 100644 --- a/syntex/Cargo.toml +++ b/syntex/Cargo.toml @@ -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" diff --git a/syntex/src/lib.rs b/syntex/src/lib.rs index a1971415..02624d84 100644 --- a/syntex/src/lib.rs +++ b/syntex/src/lib.rs @@ -1,5 +1,7 @@ extern crate syntex_syntax; +mod squash_derive; + use std::fs::File; use std::io::{self, Write}; use std::path::Path; @@ -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)); diff --git a/syntex/src/squash_derive.rs b/syntex/src/squash_derive.rs new file mode 100644 index 00000000..6e4690ed --- /dev/null +++ b/syntex/src/squash_derive.rs @@ -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, +} + +impl SquashDeriveAttrFolder { + fn into_attr(self) -> Option { + 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>, + 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 { + 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 { + 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 { + 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::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, + } + ); + } +}