mirror of
https://gitee.com/openharmony/third_party_rust_syn
synced 2024-11-27 09:50:41 +00:00
Drop the expander registry
This commit is contained in:
parent
a6ac181ae7
commit
48088d2a0e
@ -11,10 +11,8 @@ include = ["Cargo.toml", "src/**/*.rs"]
|
||||
[features]
|
||||
default = ["parsing", "printing"]
|
||||
aster = []
|
||||
expand = ["full", "parsing", "printing"]
|
||||
full = ["type-macros"]
|
||||
parsing = ["unicode-xid"]
|
||||
pretty = ["syntex_syntax"]
|
||||
printing = ["quote"]
|
||||
type-macros = []
|
||||
visit = []
|
||||
@ -22,7 +20,6 @@ visit = []
|
||||
[dependencies]
|
||||
clippy = { version = "0.*", optional = true }
|
||||
quote = { version = "0.3.0", optional = true }
|
||||
syntex_syntax = { version = "0.52.0", optional = true }
|
||||
unicode-xid = { version = "0.0.3", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
56
README.md
56
README.md
@ -182,62 +182,6 @@ full | 2 sec | The data structures representing the full AST of all possible Rus
|
||||
full, parsing | 7 sec | Parsing any valid Rust source code to an AST.
|
||||
full, printing | 4 sec | Turning an AST into Rust source code.
|
||||
full, parsing, printing | 8 sec | Parsing and printing any Rust syntax.
|
||||
full, parsing, printing, expand | 9 sec | Expansion of custom derives in a file of Rust code. This is typically what you want for expanding custom derives on stable Rust using a build script.
|
||||
full, parsing, printing, expand, pretty | 60 sec | Expansion of custom derives with pretty-printed output. This is what you want when iterating on or debugging a custom derive, but the pretty printing should be disabled once you get everything working.
|
||||
|
||||
## Custom derives on stable Rust
|
||||
|
||||
Syn supports a way of expanding custom derives from a build script, similar to
|
||||
what [Serde is able to do with serde_codegen](https://serde.rs/codegen-stable.html).
|
||||
The advantage of using Syn for this purpose rather than Syntex is much faster
|
||||
compile time.
|
||||
|
||||
Continuing with the `NumFields` example from above, it can be extended to
|
||||
support stable Rust like this. One or more custom derives are added to a
|
||||
[`Registry`](https://dtolnay.github.io/syn/syn/struct.Registry.html), which is
|
||||
then able to expand those derives in a source file at a particular path and
|
||||
write the expanded output to a different path. A custom derive is represented by
|
||||
the [`CustomDerive`](https://dtolnay.github.io/syn/syn/trait.CustomDerive.html)
|
||||
trait which takes a [`MacroInput`](https://dtolnay.github.io/syn/syn/struct.MacroInput.html)
|
||||
(either a struct or an enum) and [expands it](https://dtolnay.github.io/syn/syn/struct.Expanded.html)
|
||||
into zero or more new items and maybe a modified or unmodified instance of the
|
||||
original input.
|
||||
|
||||
```rust
|
||||
pub fn expand_file<S, D>(src: S, dst: D) -> Result<(), String>
|
||||
where S: AsRef<Path>,
|
||||
D: AsRef<Path>
|
||||
{
|
||||
let mut registry = syn::Registry::new();
|
||||
registry.add_derive("NumFields", |input| {
|
||||
let tokens = expand_num_fields(&input);
|
||||
let items = syn::parse_items(&tokens.to_string()).unwrap();
|
||||
Ok(syn::Expanded {
|
||||
new_items: items,
|
||||
original: Some(input),
|
||||
})
|
||||
});
|
||||
registry.expand_file(src, dst)
|
||||
}
|
||||
```
|
||||
|
||||
The codegen can be invoked from a build script as follows.
|
||||
|
||||
```rust
|
||||
extern crate your_codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
let src = Path::new("src/codegen_types.in.rs");
|
||||
let dst = Path::new(&out_dir).join("codegen_types.rs");
|
||||
|
||||
your_codegen::expand_file(&src, &dst).unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
|
@ -5,9 +5,6 @@
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
#[cfg(feature = "pretty")]
|
||||
extern crate syntex_syntax as syntax;
|
||||
|
||||
#[cfg(feature = "parsing")]
|
||||
extern crate unicode_xid;
|
||||
|
||||
@ -76,11 +73,6 @@ pub use macro_input::{Body, MacroInput};
|
||||
mod op;
|
||||
pub use op::{BinOp, UnOp};
|
||||
|
||||
#[cfg(feature = "expand")]
|
||||
mod registry;
|
||||
#[cfg(feature = "expand")]
|
||||
pub use registry::{CustomDerive, Expanded, Registry};
|
||||
|
||||
#[cfg(feature = "parsing")]
|
||||
mod space;
|
||||
|
||||
|
392
src/registry.rs
392
src/registry.rs
@ -1,392 +0,0 @@
|
||||
use super::{Attribute, AttrStyle, Body, Crate, Ident, Item, ItemKind, MacroInput, MetaItem,
|
||||
NestedMetaItem};
|
||||
use quote::Tokens;
|
||||
|
||||
use std::collections::BTreeMap as Map;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
/// Implementation of a custom derive. Custom derives take a struct or enum and
|
||||
/// expand it into zero or more items, typically `impl` items.
|
||||
pub trait CustomDerive {
|
||||
/// Expand the given struct or enum. If this custom derive modifies the
|
||||
/// input item or preserves it unmodified, it must be returned back in the
|
||||
/// `original` field of Expanded. The custom derive may discard the input
|
||||
/// item by setting `original` to None.
|
||||
fn expand(&self, input: MacroInput) -> Result<Expanded, String>;
|
||||
}
|
||||
|
||||
/// Produced by expanding a custom derive.
|
||||
pub struct Expanded {
|
||||
/// The items (typically `impl` items) constructed by the custom derive.
|
||||
pub new_items: Vec<Item>,
|
||||
/// The input to the custom derive, whether modified or unmodified. If the
|
||||
/// custom derive discards the input item it may do so by setting `original`
|
||||
/// to None.
|
||||
pub original: Option<MacroInput>,
|
||||
}
|
||||
|
||||
/// Registry of custom derives. Callers add custom derives to a registry, then
|
||||
/// use the registry to expand those derives in a source file.
|
||||
#[derive(Default)]
|
||||
pub struct Registry<'a> {
|
||||
derives: Map<String, Box<CustomDerive + 'a>>,
|
||||
}
|
||||
|
||||
impl<T> CustomDerive for T
|
||||
where T: Fn(MacroInput) -> Result<Expanded, String>
|
||||
{
|
||||
fn expand(&self, input: MacroInput) -> Result<Expanded, String> {
|
||||
self(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Registry<'a> {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Register a custom derive. A `fn(MacroInput) -> Result<Expanded, String>`
|
||||
/// may be used as a custom derive.
|
||||
///
|
||||
/// ```ignore
|
||||
/// registry.add_derive("Serialize", expand_serialize);
|
||||
/// ```
|
||||
pub fn add_derive<T>(&mut self, name: &str, derive: T)
|
||||
where T: CustomDerive + 'a
|
||||
{
|
||||
self.derives.insert(name.into(), Box::new(derive));
|
||||
}
|
||||
|
||||
/// Read Rust source code from the `src` file, expand the custom derives
|
||||
/// that have been registered, and write the result to the `dst` file.
|
||||
pub fn expand_file<S, D>(&self, src: S, dst: D) -> Result<(), String>
|
||||
where S: AsRef<Path>,
|
||||
D: AsRef<Path>
|
||||
{
|
||||
// Open the src file
|
||||
let mut src = match File::open(src) {
|
||||
Ok(open) => open,
|
||||
Err(err) => return Err(err.to_string()),
|
||||
};
|
||||
|
||||
// Read the contents of the src file to a String
|
||||
let mut content = String::new();
|
||||
if let Err(err) = src.read_to_string(&mut content) {
|
||||
return Err(err.to_string());
|
||||
}
|
||||
|
||||
// Parse the contents
|
||||
let krate = try!(super::parse_crate(&content));
|
||||
|
||||
// Expand
|
||||
let expanded = try!(expand_crate(self, krate));
|
||||
|
||||
// Print the expanded code to a String
|
||||
let out = try!(pretty(quote!(#expanded)));
|
||||
|
||||
// Create or truncate the dst file, opening in write-only mode
|
||||
let mut dst = match File::create(dst) {
|
||||
Ok(create) => create,
|
||||
Err(err) => return Err(err.to_string()),
|
||||
};
|
||||
|
||||
// Write expanded code to the dst file
|
||||
if let Err(err) = dst.write_all(out.as_bytes()) {
|
||||
return Err(err.to_string());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_crate(reg: &Registry, krate: Crate) -> Result<Crate, String> {
|
||||
let mut items = Vec::new();
|
||||
for item in krate.items {
|
||||
try!(expand_item(reg, item, Vec::new(), &mut items));
|
||||
}
|
||||
Ok(Crate { items: items, ..krate })
|
||||
}
|
||||
|
||||
fn expand_item(reg: &Registry,
|
||||
mut item: Item,
|
||||
cfg: Vec<NestedMetaItem>,
|
||||
out: &mut Vec<Item>)
|
||||
-> Result<(), String> {
|
||||
let (body, generics) = match item.node {
|
||||
ItemKind::Enum(variants, generics) => (Body::Enum(variants), generics),
|
||||
ItemKind::Struct(variant_data, generics) => (Body::Struct(variant_data), generics),
|
||||
_ => {
|
||||
// Custom derives cannot apply to this item, preserve it unmodified
|
||||
item.attrs.extend(combine_cfgs(cfg));
|
||||
out.push(item);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let macro_input = MacroInput {
|
||||
ident: item.ident,
|
||||
vis: item.vis,
|
||||
attrs: item.attrs,
|
||||
generics: generics,
|
||||
body: body,
|
||||
};
|
||||
expand_macro_input(reg, macro_input, cfg, out)
|
||||
}
|
||||
|
||||
fn expand_macro_input(reg: &Registry,
|
||||
mut input: MacroInput,
|
||||
inherited_cfg: Vec<NestedMetaItem>,
|
||||
out: &mut Vec<Item>)
|
||||
-> Result<(), String> {
|
||||
let mut derives = Vec::new();
|
||||
let mut all_cfg = inherited_cfg;
|
||||
|
||||
// Find custom derives on this item, removing them from the input
|
||||
input.attrs = input.attrs
|
||||
.into_iter()
|
||||
.flat_map(|attr| {
|
||||
let (new_derives, cfg, attr) = parse_attr(reg, attr);
|
||||
derives.extend(new_derives);
|
||||
all_cfg.extend(cfg);
|
||||
attr
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Expand each custom derive
|
||||
for derive in derives {
|
||||
let expanded = try!(reg.derives[derive.name.as_ref()].expand(input));
|
||||
|
||||
for new_item in expanded.new_items {
|
||||
let mut extended_cfg = all_cfg.clone();
|
||||
extended_cfg.extend(derive.cfg.clone());
|
||||
try!(expand_item(reg, new_item, extended_cfg, out));
|
||||
}
|
||||
|
||||
input = match expanded.original {
|
||||
Some(input) => input,
|
||||
None => return Ok(()),
|
||||
};
|
||||
}
|
||||
|
||||
input.attrs.extend(combine_cfgs(all_cfg));
|
||||
out.push(input.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct Derive {
|
||||
name: Ident,
|
||||
/// If the custom derive was behind a cfg_attr
|
||||
cfg: Option<NestedMetaItem>,
|
||||
}
|
||||
|
||||
/// Pull custom derives and cfgs out of the given Attribute.
|
||||
fn parse_attr(reg: &Registry,
|
||||
attr: Attribute)
|
||||
-> (Vec<Derive>, Vec<NestedMetaItem>, Option<Attribute>) {
|
||||
if attr.style != AttrStyle::Outer || attr.is_sugared_doc {
|
||||
return (Vec::new(), Vec::new(), Some(attr));
|
||||
}
|
||||
|
||||
let (name, nested) = match attr.value {
|
||||
MetaItem::List(name, nested) => (name, nested),
|
||||
_ => return (Vec::new(), Vec::new(), Some(attr)),
|
||||
};
|
||||
|
||||
match name.as_ref() {
|
||||
"derive" => {
|
||||
let (derives, attr) = parse_derive_attr(reg, nested);
|
||||
let derives = derives.into_iter()
|
||||
.map(|d| {
|
||||
Derive {
|
||||
name: d,
|
||||
cfg: None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
(derives, Vec::new(), attr)
|
||||
}
|
||||
"cfg_attr" => {
|
||||
let (derives, attr) = parse_cfg_attr(reg, nested);
|
||||
(derives, Vec::new(), attr)
|
||||
}
|
||||
"cfg" => (Vec::new(), nested, None),
|
||||
_ => {
|
||||
// Rebuild the original attribute because it was destructured above
|
||||
let attr = Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value: MetaItem::List(name, nested),
|
||||
is_sugared_doc: false,
|
||||
};
|
||||
(Vec::new(), Vec::new(), Some(attr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Assuming the given nested meta-items came from a #[derive(...)] attribute,
|
||||
/// pull out the ones that are custom derives.
|
||||
fn parse_derive_attr(reg: &Registry,
|
||||
nested: Vec<NestedMetaItem>)
|
||||
-> (Vec<Ident>, Option<Attribute>) {
|
||||
let mut derives = Vec::new();
|
||||
|
||||
let remaining: Vec<_> = nested.into_iter()
|
||||
.flat_map(|meta| {
|
||||
let word = match meta {
|
||||
NestedMetaItem::MetaItem(MetaItem::Word(word)) => word,
|
||||
_ => return Some(meta),
|
||||
};
|
||||
if reg.derives.contains_key(word.as_ref()) {
|
||||
derives.push(word);
|
||||
None
|
||||
} else {
|
||||
Some(NestedMetaItem::MetaItem(MetaItem::Word(word)))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let attr = if remaining.is_empty() {
|
||||
// Elide an empty #[derive()]
|
||||
None
|
||||
} else {
|
||||
Some(Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value: MetaItem::List("derive".into(), remaining),
|
||||
is_sugared_doc: false,
|
||||
})
|
||||
};
|
||||
|
||||
(derives, attr)
|
||||
}
|
||||
|
||||
/// Assuming the given nested meta-items came from a #[cfg_attr(...)] attribute,
|
||||
/// pull out any custom derives contained within.
|
||||
fn parse_cfg_attr(reg: &Registry, nested: Vec<NestedMetaItem>) -> (Vec<Derive>, Option<Attribute>) {
|
||||
if nested.len() != 2 {
|
||||
let attr = Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value: MetaItem::List("cfg_attr".into(), nested),
|
||||
is_sugared_doc: false,
|
||||
};
|
||||
return (Vec::new(), Some(attr));
|
||||
}
|
||||
|
||||
let mut iter = nested.into_iter();
|
||||
let cfg = iter.next().unwrap();
|
||||
let arg = iter.next().unwrap();
|
||||
|
||||
let (name, nested) = match arg {
|
||||
NestedMetaItem::MetaItem(MetaItem::List(name, nested)) => (name, nested),
|
||||
_ => {
|
||||
let attr = Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value: MetaItem::List("cfg_attr".into(), vec![cfg, arg]),
|
||||
is_sugared_doc: false,
|
||||
};
|
||||
return (Vec::new(), Some(attr));
|
||||
}
|
||||
};
|
||||
|
||||
if name == "derive" {
|
||||
let (derives, attr) = parse_derive_attr(reg, nested);
|
||||
let derives = derives.into_iter()
|
||||
.map(|d| {
|
||||
Derive {
|
||||
name: d,
|
||||
cfg: Some(cfg.clone()),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let attr = attr.map(|attr| {
|
||||
Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value: MetaItem::List("cfg_attr".into(),
|
||||
vec![cfg, NestedMetaItem::MetaItem(attr.value)]),
|
||||
is_sugared_doc: false,
|
||||
}
|
||||
});
|
||||
(derives, attr)
|
||||
} else {
|
||||
let attr = Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value:
|
||||
MetaItem::List("cfg_attr".into(),
|
||||
vec![cfg, NestedMetaItem::MetaItem(MetaItem::List(name, nested))]),
|
||||
is_sugared_doc: false,
|
||||
};
|
||||
(Vec::new(), Some(attr))
|
||||
}
|
||||
}
|
||||
|
||||
/// Combine a list of cfg expressions into an attribute like `#[cfg(a)]` or
|
||||
/// `#[cfg(all(a, b, c))]`, or nothing if there are no cfg expressions.
|
||||
fn combine_cfgs(cfg: Vec<NestedMetaItem>) -> Option<Attribute> {
|
||||
// Flatten `all` cfgs so we don't nest `all` inside of `all`.
|
||||
let cfg: Vec<_> = cfg.into_iter()
|
||||
.flat_map(|cfg| {
|
||||
let (name, nested) = match cfg {
|
||||
NestedMetaItem::MetaItem(MetaItem::List(name, nested)) => (name, nested),
|
||||
_ => return vec![cfg],
|
||||
};
|
||||
if name == "all" {
|
||||
nested
|
||||
} else {
|
||||
vec![NestedMetaItem::MetaItem(MetaItem::List(name, nested))]
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let value = match cfg.len() {
|
||||
0 => return None,
|
||||
1 => cfg,
|
||||
_ => vec![NestedMetaItem::MetaItem(MetaItem::List("all".into(), cfg))],
|
||||
};
|
||||
|
||||
Some(Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value: MetaItem::List("cfg".into(), value),
|
||||
is_sugared_doc: false,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pretty"))]
|
||||
fn pretty(tokens: Tokens) -> Result<String, String> {
|
||||
Ok(tokens.to_string())
|
||||
}
|
||||
|
||||
#[cfg(feature = "pretty")]
|
||||
fn pretty(tokens: Tokens) -> Result<String, String> {
|
||||
use syntax::parse::{self, ParseSess};
|
||||
use syntax::print::pprust;
|
||||
|
||||
let name = "syn".to_string();
|
||||
let source = tokens.to_string();
|
||||
let sess = ParseSess::new();
|
||||
let krate = match parse::parse_crate_from_source_str(name, source, &sess) {
|
||||
Ok(krate) => krate,
|
||||
Err(mut err) => {
|
||||
err.emit();
|
||||
return Err("pretty printer failed to parse expanded code".into());
|
||||
}
|
||||
};
|
||||
|
||||
if sess.span_diagnostic.has_errors() {
|
||||
return Err("pretty printer failed to parse expanded code".into());
|
||||
}
|
||||
|
||||
let mut reader = &tokens.to_string().into_bytes()[..];
|
||||
let mut writer = Vec::new();
|
||||
let ann = pprust::NoAnn;
|
||||
|
||||
try!(pprust::print_crate(
|
||||
sess.codemap(),
|
||||
&sess.span_diagnostic,
|
||||
&krate,
|
||||
"".to_string(),
|
||||
&mut reader,
|
||||
Box::new(&mut writer),
|
||||
&ann,
|
||||
false).map_err(|err| err.to_string()));
|
||||
|
||||
String::from_utf8(writer).map_err(|err| err.to_string())
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
#![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");
|
||||
let krate = parse_crate(&expanded).expect("parse output file");
|
||||
|
||||
assert_eq!(quote!(#krate), expected);
|
||||
}
|
||||
|
||||
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),
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user