提交 956e2bcb 编写于 作者: B bors

Auto merge of #39572 - jseyfried:fix_inert_attributes, r=nrc

macros: fix inert attributes from `proc_macro_derives` with `#![feature(proc_macro)]`

This PR refactors collection of `proc_macro_derive` invocations to fix #39347.

After this PR, the input to a `#[proc_macro_derive]` function no longer sees `#[derive]`s on the underlying item. For example, consider:
```rust
extern crate my_derives;
use my_derives::{Trait, Trait2};

#[derive(Copy, Clone)]
#[derive(Trait)]
#[derive(Trait2)]
struct S;
```

Today, the input to the `Trait` derive is `#[derive(Copy, Clone, Trait2)] struct S;`, and the input to the `Trait2` derive is `#[derive(Copy, Clone)] struct S;`. More generally, a `proc_macro_derive` sees all builtin derives, as well as all `proc_macro_derive`s listed *after* the one being invoked.

After this PR, both `Trait` and `Trait2` will see `struct S;`.
This is a [breaking-change], but I believe it is highly unlikely to cause breakage in practice.

r? @nrc
......@@ -616,10 +616,9 @@ fn register_custom_derive(&mut self,
trait_name: &str,
expand: fn(TokenStream) -> TokenStream,
attributes: &[&'static str]) {
let attrs = attributes.iter().cloned().map(Symbol::intern).collect();
let derive = SyntaxExtension::ProcMacroDerive(
Box::new(ProcMacroDerive::new(expand, attrs))
);
let attrs = attributes.iter().cloned().map(Symbol::intern).collect::<Vec<_>>();
let derive = ProcMacroDerive::new(expand, attrs.clone());
let derive = SyntaxExtension::ProcMacroDerive(Box::new(derive), attrs);
self.0.push((Symbol::intern(trait_name), Rc::new(derive)));
}
......
......@@ -27,10 +27,10 @@
use syntax::ext::expand::{Expansion, mark_tts};
use syntax::ext::hygiene::Mark;
use syntax::ext::tt::macro_rules;
use syntax::feature_gate::{emit_feature_err, GateIssue, is_builtin_attr};
use syntax::feature_gate::{self, emit_feature_err, GateIssue};
use syntax::fold::{self, Folder};
use syntax::ptr::P;
use syntax::symbol::keywords;
use syntax::symbol::{Symbol, keywords};
use syntax::util::lev_distance::find_best_match_for_name;
use syntax::visit::Visitor;
use syntax_pos::{Span, DUMMY_SP};
......@@ -130,12 +130,16 @@ fn is_whitelisted_legacy_custom_derive(&self, name: Name) -> bool {
self.whitelisted_legacy_custom_derives.contains(&name)
}
fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) {
fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion, derives: &[Mark]) {
let invocation = self.invocations[&mark];
self.collect_def_ids(invocation, expansion);
self.current_module = invocation.module.get();
self.current_module.unresolved_invocations.borrow_mut().remove(&mark);
self.current_module.unresolved_invocations.borrow_mut().extend(derives);
for &derive in derives {
self.invocations.insert(derive, invocation);
}
let mut visitor = BuildReducedGraphVisitor {
resolver: self,
legacy_scope: LegacyScope::Invocation(invocation),
......@@ -172,7 +176,9 @@ fn resolve_imports(&mut self) {
ImportResolver { resolver: self }.resolve_imports()
}
fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
// Resolves attribute and derive legacy macros from `#![plugin(..)]`.
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>)
-> Option<ast::Attribute> {
for i in 0..attrs.len() {
match self.builtin_macros.get(&attrs[i].name()).cloned() {
Some(binding) => match *binding.get_macro(self) {
......@@ -183,11 +189,50 @@ fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::At
},
None => {}
}
}
if self.proc_macro_enabled && !is_builtin_attr(&attrs[i]) {
return Some(attrs.remove(i));
// Check for legacy derives
for i in 0..attrs.len() {
if attrs[i].name() == "derive" {
let mut traits = match attrs[i].meta_item_list() {
Some(traits) if !traits.is_empty() => traits.to_owned(),
_ => continue,
};
for j in 0..traits.len() {
let legacy_name = Symbol::intern(&match traits[j].word() {
Some(..) => format!("derive_{}", traits[j].name().unwrap()),
None => continue,
});
if !self.builtin_macros.contains_key(&legacy_name) {
continue
}
let span = traits.remove(j).span;
self.gate_legacy_custom_derive(legacy_name, span);
if traits.is_empty() {
attrs.remove(i);
} else {
attrs[i].value = ast::MetaItem {
name: attrs[i].name(),
span: attrs[i].span,
node: ast::MetaItemKind::List(traits),
};
}
return Some(ast::Attribute {
value: ast::MetaItem {
name: legacy_name,
span: span,
node: ast::MetaItemKind::Word,
},
id: attr::mk_attr_id(),
style: ast::AttrStyle::Outer,
is_sugared_doc: false,
span: span,
});
}
}
}
None
}
......@@ -236,7 +281,7 @@ fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
Ok(binding) => Ok(binding.get_macro(self)),
Err(Determinacy::Undetermined) if !force => return Err(Determinacy::Undetermined),
_ => {
let msg = format!("macro undefined: '{}!'", name);
let msg = format!("macro undefined: `{}`", name);
let mut err = self.session.struct_span_err(span, &msg);
self.suggest_macro_name(&name.as_str(), &mut err);
err.emit();
......@@ -251,13 +296,6 @@ fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
result
}
fn resolve_builtin_macro(&mut self, tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy> {
match self.builtin_macros.get(&tname).cloned() {
Some(binding) => Ok(binding.get_macro(self)),
None => Err(Determinacy::Undetermined),
}
}
fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
-> Result<Rc<SyntaxExtension>, Determinacy> {
let ast::Path { span, .. } = *path;
......@@ -540,4 +578,14 @@ fn err_if_macro_use_proc_macro(&mut self, name: Name, use_span: Span,
`use {}::{};`", crate_name, name))
.emit();
}
fn gate_legacy_custom_derive(&mut self, name: Symbol, span: Span) {
if !self.session.features.borrow().custom_derive {
let sess = &self.session.parse_sess;
let explain = feature_gate::EXPLAIN_CUSTOM_DERIVE;
emit_feature_err(sess, "custom_derive", span, GateIssue::Language, explain);
} else if !self.is_whitelisted_legacy_custom_derive(name) {
self.session.span_warn(span, feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE);
}
}
}
......@@ -514,7 +514,7 @@ pub enum SyntaxExtension {
/// The input is the annotated item.
/// Allows generating code to implement a Trait for a given struct
/// or enum item.
ProcMacroDerive(Box<MultiItemModifier>),
ProcMacroDerive(Box<MultiItemModifier>, Vec<Symbol> /* inert attribute names */),
/// An attribute-like procedural macro that derives a builtin trait.
BuiltinDerive(BuiltinDeriveFn),
......@@ -528,15 +528,15 @@ pub trait Resolver {
fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item>;
fn is_whitelisted_legacy_custom_derive(&self, name: Name) -> bool;
fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion);
fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion, derives: &[Mark]);
fn add_ext(&mut self, ident: ast::Ident, ext: Rc<SyntaxExtension>);
fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>);
fn resolve_imports(&mut self);
fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
// Resolves attribute and derive legacy macros from `#![plugin(..)]`.
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
-> Result<Rc<SyntaxExtension>, Determinacy>;
fn resolve_builtin_macro(&mut self, tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy>;
fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
-> Result<Rc<SyntaxExtension>, Determinacy>;
}
......@@ -555,19 +555,16 @@ fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() }
fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> { item }
fn is_whitelisted_legacy_custom_derive(&self, _name: Name) -> bool { false }
fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {}
fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion, _derives: &[Mark]) {}
fn add_ext(&mut self, _ident: ast::Ident, _ext: Rc<SyntaxExtension>) {}
fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec<Mark>) {}
fn resolve_imports(&mut self) {}
fn find_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None }
fn find_legacy_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None }
fn resolve_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool)
-> Result<Rc<SyntaxExtension>, Determinacy> {
Err(Determinacy::Determined)
}
fn resolve_builtin_macro(&mut self, _tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy> {
Err(Determinacy::Determined)
}
fn resolve_derive_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool)
-> Result<Rc<SyntaxExtension>, Determinacy> {
Err(Determinacy::Determined)
......
......@@ -8,132 +8,42 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use ast::Name;
use attr;
use ast::{self, NestedMetaItem}; use ext::base::{ExtCtxt, SyntaxExtension};
use codemap;
use attr::HasAttrs;
use {ast, codemap};
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use feature_gate;
use symbol::Symbol;
use syntax_pos::Span;
pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute)
-> Option<&'a NestedMetaItem> {
if attr.name() != "derive" {
return None;
}
if attr.value_str().is_some() {
cx.span_err(attr.span, "unexpected value in `derive`");
return None;
}
let traits = attr.meta_item_list().unwrap_or(&[]);
if traits.is_empty() {
cx.span_warn(attr.span, "empty trait list in `derive`");
return None;
}
return traits.get(0);
}
pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &[ast::Attribute]) {
for attr in attrs {
pub fn collect_derives(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) -> Vec<(Symbol, Span)> {
let mut result = Vec::new();
attrs.retain(|attr| {
if attr.name() != "derive" {
continue;
return true;
}
if attr.value_str().is_some() {
cx.span_err(attr.span, "unexpected value in `derive`");
return false;
}
let traits = attr.meta_item_list().unwrap_or(&[]).to_owned();
if traits.is_empty() {
cx.span_warn(attr.span, "empty trait list in `derive`");
attr::mark_used(&attr);
continue;
return false;
}
for titem in traits {
if titem.word().is_none() {
cx.span_err(titem.span, "malformed `derive` entry");
return false;
}
}
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum DeriveType {
Legacy,
ProcMacro,
Builtin
}
impl DeriveType {
// Classify a derive trait name by resolving the macro.
pub fn classify(cx: &mut ExtCtxt, tname: Name) -> DeriveType {
let legacy_derive_name = Symbol::intern(&format!("derive_{}", tname));
if let Ok(_) = cx.resolver.resolve_builtin_macro(legacy_derive_name) {
return DeriveType::Legacy;
}
match cx.resolver.resolve_builtin_macro(tname) {
Ok(ext) => match *ext {
SyntaxExtension::BuiltinDerive(..) => DeriveType::Builtin,
_ => DeriveType::ProcMacro,
},
Err(_) => DeriveType::ProcMacro,
}
}
}
pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>,
derive_type: DeriveType) -> Option<ast::Attribute> {
for i in 0..attrs.len() {
if attrs[i].name() != "derive" {
continue;
}
if attrs[i].value_str().is_some() {
continue;
}
let mut traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned();
// First, weed out malformed #[derive]
traits.retain(|titem| titem.word().is_some());
let mut titem = None;
// See if we can find a matching trait.
for j in 0..traits.len() {
let tname = match traits[j].name() {
Some(tname) => tname,
_ => continue,
};
if DeriveType::classify(cx, tname) == derive_type {
titem = Some(traits.remove(j));
break;
}
result.push((titem.name().unwrap(), titem.span));
}
// If we find a trait, remove the trait from the attribute.
if let Some(titem) = titem {
if traits.len() == 0 {
attrs.remove(i);
} else {
let derive = Symbol::intern("derive");
let mitem = cx.meta_list(titem.span, derive, traits);
attrs[i] = cx.attribute(titem.span, mitem);
}
let derive = Symbol::intern("derive");
let mitem = cx.meta_list(titem.span, derive, vec![titem]);
return Some(cx.attribute(mitem.span, mitem));
}
}
return None;
true
});
result
}
fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {
......@@ -150,69 +60,25 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {
}
}
pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) {
if attrs.is_empty() {
return;
}
let titems = attrs.iter().filter(|a| {
a.name() == "derive"
}).flat_map(|a| {
a.meta_item_list().unwrap_or(&[]).iter()
}).filter_map(|titem| {
titem.name()
}).collect::<Vec<_>>();
let span = attrs[0].span;
if !attrs.iter().any(|a| a.name() == "structural_match") &&
titems.iter().any(|t| *t == "PartialEq") && titems.iter().any(|t| *t == "Eq") {
let structural_match = Symbol::intern("structural_match");
let span = allow_unstable(cx, span, "derive(PartialEq, Eq)");
let meta = cx.meta_word(span, structural_match);
attrs.push(cx.attribute(span, meta));
}
if !attrs.iter().any(|a| a.name() == "rustc_copy_clone_marker") &&
titems.iter().any(|t| *t == "Copy") && titems.iter().any(|t| *t == "Clone") {
let structural_match = Symbol::intern("rustc_copy_clone_marker");
let span = allow_unstable(cx, span, "derive(Copy, Clone)");
let meta = cx.meta_word(span, structural_match);
attrs.push(cx.attribute(span, meta));
}
}
pub fn find_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>)
-> Option<ast::Attribute> {
verify_derive_attrs(cx, attrs);
get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| {
let titem = derive_attr_trait(cx, &a);
titem.and_then(|titem| {
let tword = titem.word().unwrap();
let tname = tword.name();
if !cx.ecfg.enable_custom_derive() {
feature_gate::emit_feature_err(
&cx.parse_sess,
"custom_derive",
titem.span,
feature_gate::GateIssue::Language,
feature_gate::EXPLAIN_CUSTOM_DERIVE
);
None
} else {
let name = Symbol::intern(&format!("derive_{}", tname));
if !cx.resolver.is_whitelisted_legacy_custom_derive(name) {
cx.span_warn(titem.span,
feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE);
}
let mitem = cx.meta_word(titem.span, name);
Some(cx.attribute(mitem.span, mitem))
}
})
}).or_else(|| {
get_derive_attr(cx, attrs, DeriveType::ProcMacro)
}).or_else(|| {
add_derived_markers(cx, attrs);
get_derive_attr(cx, attrs, DeriveType::Builtin)
pub fn add_derived_markers<T: HasAttrs>(cx: &mut ExtCtxt, traits: &[(Symbol, Span)], item: T) -> T {
let span = match traits.get(0) {
Some(&(_, span)) => span,
None => return item,
};
item.map_attrs(|mut attrs| {
if traits.iter().any(|&(name, _)| name == "PartialEq") &&
traits.iter().any(|&(name, _)| name == "Eq") {
let span = allow_unstable(cx, span, "derive(PartialEq, Eq)");
let meta = cx.meta_word(span, Symbol::intern("structural_match"));
attrs.push(cx.attribute(span, meta));
}
if traits.iter().any(|&(name, _)| name == "Copy") &&
traits.iter().any(|&(name, _)| name == "Clone") {
let span = allow_unstable(cx, span, "derive(Copy, Clone)");
let meta = cx.meta_word(span, Symbol::intern("rustc_copy_clone_marker"));
attrs.push(cx.attribute(span, meta));
}
attrs
})
}
此差异已折叠。
......@@ -84,8 +84,17 @@ pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self {
}
}
pub fn add(&mut self, id: ast::NodeId, expansion: Expansion) {
let expansion = expansion.fold_with(self);
pub fn add(&mut self, id: ast::NodeId, expansion: Expansion, derives: Vec<Mark>) {
let mut expansion = expansion.fold_with(self);
if let Expansion::Items(mut items) = expansion {
for derive in derives {
match self.remove(derive.as_placeholder_id()) {
Expansion::Items(derived_items) => items.extend(derived_items),
_ => unreachable!(),
}
}
expansion = Expansion::Items(items);
}
self.expansions.insert(id, expansion);
}
......
......@@ -107,12 +107,10 @@ fn expand(&self,
}
});
let mut res = vec![Annotatable::Item(item)];
// Reassign spans of all expanded items to the input `item`
// for better errors here.
res.extend(new_items.into_iter().flat_map(|item| {
ChangeSpan { span: span }.fold_item(item)
}).map(Annotatable::Item));
res
new_items.into_iter().map(|item| {
Annotatable::Item(ChangeSpan { span: span }.fold_item(item).expect_one(""))
}).collect()
}
}
......@@ -39,18 +39,18 @@ fn name_of(&self, st: &str) -> ast::Name { loop { } }
pub fn main() {
let ecx = &ParseSess;
let x = quote_tokens!(ecx, 3); //~ ERROR macro undefined: 'quote_tokens!'
let x = quote_expr!(ecx, 3); //~ ERROR macro undefined: 'quote_expr!'
let x = quote_ty!(ecx, 3); //~ ERROR macro undefined: 'quote_ty!'
let x = quote_method!(ecx, 3); //~ ERROR macro undefined: 'quote_method!'
let x = quote_item!(ecx, 3); //~ ERROR macro undefined: 'quote_item!'
let x = quote_pat!(ecx, 3); //~ ERROR macro undefined: 'quote_pat!'
let x = quote_arm!(ecx, 3); //~ ERROR macro undefined: 'quote_arm!'
let x = quote_stmt!(ecx, 3); //~ ERROR macro undefined: 'quote_stmt!'
let x = quote_matcher!(ecx, 3); //~ ERROR macro undefined: 'quote_matcher!'
let x = quote_attr!(ecx, 3); //~ ERROR macro undefined: 'quote_attr!'
let x = quote_arg!(ecx, 3); //~ ERROR macro undefined: 'quote_arg!'
let x = quote_block!(ecx, 3); //~ ERROR macro undefined: 'quote_block!'
let x = quote_meta_item!(ecx, 3); //~ ERROR macro undefined: 'quote_meta_item!'
let x = quote_path!(ecx, 3); //~ ERROR macro undefined: 'quote_path!'
let x = quote_tokens!(ecx, 3); //~ ERROR macro undefined: `quote_tokens`
let x = quote_expr!(ecx, 3); //~ ERROR macro undefined: `quote_expr`
let x = quote_ty!(ecx, 3); //~ ERROR macro undefined: `quote_ty`
let x = quote_method!(ecx, 3); //~ ERROR macro undefined: `quote_method`
let x = quote_item!(ecx, 3); //~ ERROR macro undefined: `quote_item`
let x = quote_pat!(ecx, 3); //~ ERROR macro undefined: `quote_pat`
let x = quote_arm!(ecx, 3); //~ ERROR macro undefined: `quote_arm`
let x = quote_stmt!(ecx, 3); //~ ERROR macro undefined: `quote_stmt`
let x = quote_matcher!(ecx, 3); //~ ERROR macro undefined: `quote_matcher`
let x = quote_attr!(ecx, 3); //~ ERROR macro undefined: `quote_attr`
let x = quote_arg!(ecx, 3); //~ ERROR macro undefined: `quote_arg`
let x = quote_block!(ecx, 3); //~ ERROR macro undefined: `quote_block`
let x = quote_meta_item!(ecx, 3); //~ ERROR macro undefined: `quote_meta_item`
let x = quote_path!(ecx, 3); //~ ERROR macro undefined: `quote_path`
}
......@@ -14,5 +14,5 @@
extern crate macro_crate_test;
fn main() {
assert_eq!(3, unexported_macro!()); //~ ERROR macro undefined: 'unexported_macro!'
assert_eq!(3, unexported_macro!()); //~ ERROR macro undefined: `unexported_macro`
}
......@@ -12,12 +12,12 @@
// gate
__register_diagnostic!(E0001);
//~^ ERROR macro undefined: '__register_diagnostic!'
//~^ ERROR macro undefined: `__register_diagnostic`
fn main() {
__diagnostic_used!(E0001);
//~^ ERROR macro undefined: '__diagnostic_used!'
//~^ ERROR macro undefined: `__diagnostic_used`
}
__build_diagnostic_array!(DIAGNOSTICS);
//~^ ERROR macro undefined: '__build_diagnostic_array!'
//~^ ERROR macro undefined: `__build_diagnostic_array`
......@@ -10,9 +10,9 @@
fn main() {
print!(test!());
//~^ ERROR: macro undefined: 'test!'
//~^ ERROR: macro undefined: `test`
//~^^ ERROR: format argument must be a string literal
concat!(test!());
//~^ ERROR: macro undefined: 'test!'
//~^ ERROR: macro undefined: `test`
}
......@@ -11,5 +11,5 @@
fn main() {}
impl Type {
undef!(); //~ ERROR macro undefined: 'undef!'
undef!(); //~ ERROR macro undefined: `undef`
}
......@@ -16,5 +16,5 @@ fn main() {
foo!(0); // Check that we report errors at macro definition, not expansion.
let _: cfg!(foo) = (); //~ ERROR non-type macro in type position
derive!(); //~ ERROR macro undefined: 'derive!'
derive!(); //~ ERROR macro undefined: `derive`
}
......@@ -18,8 +18,8 @@ mod m {
}
fn main() {
k!(); //~ ERROR macro undefined: 'k!'
k!(); //~ ERROR macro undefined: `k`
//~^ HELP did you mean `kl!`?
kl!(); //~ ERROR macro undefined: 'kl!'
kl!(); //~ ERROR macro undefined: `kl`
//~^ HELP have you added the `#[macro_use]` on the module/import?
}
......@@ -25,7 +25,7 @@ pub fn main() {
ref mut Self => (),
//~^ ERROR expected identifier, found keyword `Self`
Self!() => (),
//~^ ERROR macro undefined: 'Self!'
//~^ ERROR macro undefined: `Self`
Foo { Self } => (),
//~^ ERROR expected identifier, found keyword `Self`
}
......
......@@ -20,6 +20,5 @@
pub fn derive(input: TokenStream) -> TokenStream {
let input = input.to_string();
assert!(input.contains("struct A;"));
assert!(input.contains("#[derive(Debug, PartialEq, Eq, Copy, Clone)]"));
"".parse().unwrap()
}
......@@ -19,6 +19,6 @@
#[proc_macro_derive(AToB)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = input.to_string();
assert_eq!(input, "#[derive(Copy, Clone)]\nstruct A;");
assert_eq!(input, "struct A;");
"struct B;".parse().unwrap()
}
......@@ -22,6 +22,5 @@ pub fn derive(input: TokenStream) -> TokenStream {
assert!(input.contains("#[B]"));
assert!(input.contains("struct B {"));
assert!(input.contains("#[C]"));
assert!(input.contains("#[derive(Debug, PartialEq, Eq, Copy, Clone)]"));
"".parse().unwrap()
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册