Expand custom derives
diff --git a/tests/test_expand.rs b/tests/test_expand.rs
new file mode 100644
index 0000000..33effd5
--- /dev/null
+++ b/tests/test_expand.rs
@@ -0,0 +1,137 @@
+#![cfg(feature = "expand")]
+
+extern crate syn;
+use syn::*;
+
+#[macro_use]
+extern crate quote;
+use quote::Tokens;
+
+extern crate tempdir;
+use tempdir::TempDir;
+
+use std::fs::File;
+use std::io::{Read, Write};
+
+#[test]
+fn test_cfg() {
+ let original = quote! {
+ use super::*;
+
+ #[derive(A)]
+ struct P;
+
+ #[cfg_attr(feature = "q", derive(A))]
+ struct Q;
+
+ #[derive(A)]
+ #[cfg(feature = "r")]
+ struct R;
+
+ #[cfg(feature = "s1")]
+ #[cfg(all(feature = "s2", feature = "s3"))]
+ #[cfg_attr(feature = "s4", derive(A))]
+ struct S;
+ };
+
+ let expected = quote! {
+ // Unmodified from the input
+ use super::*;
+
+ type P = ();
+
+ #[cfg(feature = "q")]
+ type Q = ();
+
+ #[cfg(feature = "r")]
+ type R = ();
+
+ #[cfg(all(feature = "s1", feature = "s2", feature = "s3", feature = "s4"))]
+ type S = ();
+ };
+
+ test_expand(original, expected);
+}
+
+#[test]
+fn test_recursive() {
+ let original = quote! {
+ #[d]
+ #[cfg_attr(feature = "f", derive(Copy, B, Clone))]
+ #[e]
+ #[cfg(feature = "e")]
+ struct T;
+ };
+
+ let expected = quote! {
+ // From #[derive(A)] on struct S produced by #[derive(B)]
+ #[cfg(all(feature = "e", feature = "f", feature = "g"))]
+ type S = ();
+
+ // From #[derive(B)] on struct T
+ #[cfg(all(feature = "e", feature = "f"))]
+ impl B for T {}
+
+ // From the input
+ #[d]
+ #[cfg_attr(feature = "f", derive(Copy, Clone))]
+ #[e]
+ #[cfg(feature = "e")]
+ struct T;
+ };
+
+ test_expand(original, expected);
+}
+
+fn test_expand(original: Tokens, expected: Tokens) {
+ let dir = TempDir::new("syn").expect("create temp dir");
+ let src_path = dir.path().join("expand.in.rs");
+ let dst_path = dir.path().join("expand.rs");
+
+ // Write the src file
+ let mut src_file = File::create(&src_path).expect("create temp file");
+ src_file.write_all(original.to_string().as_bytes()).expect("write temp file");
+
+ // Run expansion
+ let mut registry = Registry::new();
+ registry.add_derive("A", expand_a);
+ registry.add_derive("B", expand_b);
+ registry.expand_file(&src_path, &dst_path).unwrap();
+
+ // Read the dst file
+ let mut expanded = String::new();
+ let mut dst_file = File::open(&dst_path).expect("open output file");
+ dst_file.read_to_string(&mut expanded).expect("read output file");
+
+ assert_eq!(expanded, expected.to_string());
+}
+
+fn expand_a(input: MacroInput) -> Result<Expanded, String> {
+ let name = &input.ident;
+ let out = quote! {
+ type #name = ();
+ };
+ Ok(Expanded {
+ new_items: parse_items(&out.to_string()).unwrap(),
+ original: None,
+ })
+}
+
+fn expand_b(input: MacroInput) -> Result<Expanded, String> {
+ assert_eq!(quote!(#input), quote! {
+ #[d]
+ #[cfg_attr(feature = "f", derive(Copy, Clone))]
+ #[e]
+ struct T;
+ });
+ let out = quote! {
+ #[cfg_attr(feature = "g", derive(A))]
+ struct S;
+
+ impl B for T {}
+ };
+ Ok(Expanded {
+ new_items: parse_items(&out.to_string()).unwrap(),
+ original: Some(input),
+ })
+}