Skip to content
This repository was archived by the owner on Jun 21, 2020. It is now read-only.

Commit b3a0a9a

Browse files
committed
added interpretation of the attr tokens passed to the pub_interface macro
* If applied to a trait, the tokens are interpreted as the type that implements the trait (this affects code generation, which otherwise assumed the implementor was named `Contract`) * If applied to an impl, and there are extra `attr` tokens, an error is issued.
1 parent 390e8a2 commit b3a0a9a

File tree

3 files changed

+185
-10
lines changed

3 files changed

+185
-10
lines changed

eng-wasm/derive/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ use pub_interface::impl_pub_interface;
3838
/// all methods declared as pub will be exported by the contract. if placed on an impl block,
3939
/// the implementing struct can have any name you choose.
4040
#[proc_macro_attribute]
41-
pub fn pub_interface(_attr: TokenStream, item: TokenStream) -> TokenStream {
42-
impl_pub_interface(item.into()).into()
41+
pub fn pub_interface(attr: TokenStream, item: TokenStream) -> TokenStream {
42+
impl_pub_interface(attr.into(), item.into()).into()
4343
}
4444

4545
#[proc_macro_attribute]

eng-wasm/derive/src/pub_interface.rs

Lines changed: 155 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use syn::spanned::Spanned;
44
mod parse_signatures;
55

66
use super::into_ident::IntoIdent;
7-
use parse_signatures::PubInterfaceSignatures;
7+
use parse_signatures::{ParseError, PubInterfaceItemType, PubInterfaceSignatures};
88

99
const DEFAULT_IMPLEMENTOR_NAME: &str = "Contract";
1010
const CONSTRUCTOR_NAME: &str = "construct";
@@ -15,9 +15,15 @@ const FUNCTION_NAME_FUNC_NAME: &str = "function_name";
1515
const ARGS_FUNC_NAME: &str = "args";
1616
const CALL_FUNC_NAME: &str = "call";
1717

18-
pub(crate) fn impl_pub_interface(item: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
18+
pub(crate) fn impl_pub_interface(
19+
attr: proc_macro2::TokenStream,
20+
item: proc_macro2::TokenStream,
21+
) -> proc_macro2::TokenStream {
1922
let cloned_item = item.clone();
20-
let pub_interface_signatures = parse_macro_input2!(cloned_item as PubInterfaceSignatures);
23+
let mut pub_interface_signatures = parse_macro_input2!(cloned_item as PubInterfaceSignatures);
24+
if let Err(error) = apply_macro_attr(attr, &mut pub_interface_signatures) {
25+
return error.to_compile_error();
26+
}
2127

2228
let deploy_func_name = DEPLOY_FUNC_NAME.into_ident();
2329
let dispatch_func_name = DISPATCH_FUNC_NAME.into_ident();
@@ -44,6 +50,51 @@ pub(crate) fn impl_pub_interface(item: proc_macro2::TokenStream) -> proc_macro2:
4450
}
4551
}
4652

53+
/// Parse the arguments to the macro and apply them to the `PubInterfaceSignatures`
54+
///
55+
/// The arguments to the macro are the parts written between parenthesis in the macro invocation.
56+
///
57+
/// # Examples:
58+
///
59+
/// (This never actually gets compiled for some reason. You can try doing
60+
/// `rustdoc --test src/lib.rs` but that generated compilation errors.
61+
/// Probably because this is a pro-macro crate.)
62+
///
63+
/// ```
64+
/// # use eng_wasm_derive::pub_interface;
65+
///
66+
/// #[pub_interface(MyContract)]
67+
/// trait ContractInterface {
68+
/// fn foo();
69+
/// }
70+
///
71+
/// struct MyContract;
72+
///
73+
/// impl ContractInterface for MyContract {
74+
/// fn foo() {}
75+
/// }
76+
/// ```
77+
fn apply_macro_attr(
78+
attr: proc_macro2::TokenStream,
79+
pub_interface_signatures: &mut PubInterfaceSignatures,
80+
) -> syn::Result<()> {
81+
if attr.is_empty() {
82+
return Ok(());
83+
}
84+
85+
match pub_interface_signatures.item_type {
86+
PubInterfaceItemType::ItemTrait => {
87+
let implementor: syn::Type = syn::parse2(attr)?;
88+
pub_interface_signatures.implementor = implementor;
89+
Ok(())
90+
}
91+
PubInterfaceItemType::ItemImpl => Err(syn::Error::new_spanned(
92+
attr,
93+
ParseError::CustomImplementorOnImpl,
94+
)),
95+
}
96+
}
97+
4798
fn generate_eng_wasm_aux_functions(
4899
function_name_func_name: &syn::Ident,
49100
args_func_name: &syn::Ident,
@@ -250,6 +301,107 @@ fn generate_dispatch_function(
250301
mod tests {
251302
use super::*;
252303

304+
#[test]
305+
fn apply_macro_attr_works_on_trait() {
306+
let attr_tokens = quote!(Bar);
307+
308+
let mut pub_interface_signatures = PubInterfaceSignatures {
309+
signatures: vec![],
310+
implementor: syn::parse2(quote!(Foo)).unwrap(),
311+
item_type: PubInterfaceItemType::ItemTrait,
312+
};
313+
314+
apply_macro_attr(attr_tokens.clone(), &mut pub_interface_signatures).unwrap();
315+
316+
assert_eq!(
317+
syn::parse2::<syn::Type>(attr_tokens).unwrap(),
318+
pub_interface_signatures.implementor
319+
);
320+
}
321+
322+
#[test]
323+
fn apply_macro_attr_with_empty_input_works_on_trait() {
324+
let attr_tokens = quote!(); // This is empty
325+
326+
let mut pub_interface_signatures = PubInterfaceSignatures {
327+
signatures: vec![],
328+
implementor: syn::parse2(quote!(Foo)).unwrap(),
329+
item_type: PubInterfaceItemType::ItemTrait,
330+
};
331+
332+
apply_macro_attr(attr_tokens.clone(), &mut pub_interface_signatures).unwrap();
333+
334+
// Check that the implementor did not change
335+
assert_eq!(
336+
syn::parse2::<syn::Type>(quote!(Foo)).unwrap(),
337+
pub_interface_signatures.implementor
338+
);
339+
}
340+
341+
#[test]
342+
fn apply_macro_attr_with_bad_input_fails_on_trait() {
343+
let attr_tokens = quote!(this is not a valid type);
344+
345+
let mut pub_interface_signatures = PubInterfaceSignatures {
346+
signatures: vec![],
347+
implementor: syn::parse2(quote!(Foo)).unwrap(),
348+
item_type: PubInterfaceItemType::ItemTrait,
349+
};
350+
351+
let error =
352+
apply_macro_attr(attr_tokens.clone(), &mut pub_interface_signatures).unwrap_err();
353+
354+
assert_eq!(error.to_string(), "unexpected token");
355+
// Check that the implementor did not change
356+
assert_eq!(
357+
syn::parse2::<syn::Type>(quote!(Foo)).unwrap(),
358+
pub_interface_signatures.implementor
359+
);
360+
}
361+
362+
#[test]
363+
fn apply_macro_attr_with_empty_input_works_on_impl() {
364+
let attr_tokens = quote!(); // This is empty
365+
366+
let mut pub_interface_signatures = PubInterfaceSignatures {
367+
signatures: vec![],
368+
implementor: syn::parse2(quote!(Foo)).unwrap(),
369+
item_type: PubInterfaceItemType::ItemImpl,
370+
};
371+
372+
apply_macro_attr(attr_tokens.clone(), &mut pub_interface_signatures).unwrap();
373+
374+
// Check that the implementor did not change
375+
assert_eq!(
376+
syn::parse2::<syn::Type>(quote!(Foo)).unwrap(),
377+
pub_interface_signatures.implementor
378+
);
379+
}
380+
381+
#[test]
382+
fn apply_macro_attr_fails_on_impl() {
383+
let attr_tokens = quote!(Bar);
384+
385+
let mut pub_interface_signatures = PubInterfaceSignatures {
386+
signatures: vec![],
387+
implementor: syn::parse2(quote!(Foo)).unwrap(),
388+
item_type: PubInterfaceItemType::ItemImpl,
389+
};
390+
391+
let error =
392+
apply_macro_attr(attr_tokens.clone(), &mut pub_interface_signatures).unwrap_err();
393+
394+
assert_eq!(
395+
error.to_string(),
396+
ParseError::CustomImplementorOnImpl.to_string()
397+
);
398+
// Check that the implementor did not change
399+
assert_eq!(
400+
syn::parse2::<syn::Type>(quote!(Foo)).unwrap(),
401+
pub_interface_signatures.implementor
402+
);
403+
}
404+
253405
#[test]
254406
fn deploy_generation() -> syn::Result<()> {
255407
let input = quote!(

eng-wasm/derive/src/pub_interface/parse_signatures.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::pub_interface::CONSTRUCTOR_NAME;
1414
///
1515
/// This type mostly exists to pass into syn::Error as an error message.
1616
#[derive(Display, Debug)]
17-
enum ParseError {
17+
pub(crate) enum ParseError {
1818
#[display("pub_interface item must be either a trait or an inherent struct impl")]
1919
BadInputItem,
2020

@@ -47,13 +47,29 @@ enum ParseError {
4747

4848
#[display("methods in impls annotated with pub_interface should be either `fn` or `pub fn`")]
4949
ImplMethodWithBadVisibility,
50+
51+
#[display("custom implementors are not supported when pub_interface is applied to `impl`s")]
52+
CustomImplementorOnImpl,
5053
}
5154

55+
/// This enum is used to present the result of the speculative parsing inside
56+
/// `impl syn::parse::Parse for PubInterfaceSignatures` which tries to parse the macro input
57+
/// in one of several ways
5258
enum PubInterfaceInput {
5359
ItemTrait(syn::ItemTrait),
5460
ItemImpl(syn::ItemImpl),
5561
}
5662

63+
/// This enum is used to record what kind of item the macro was applied to.
64+
///
65+
/// This information is used later when considering the macro `attr` to modify the
66+
/// `PubInterfaceSignatures` that was parsed in this module.
67+
#[derive(Copy, Clone)]
68+
pub(crate) enum PubInterfaceItemType {
69+
ItemTrait,
70+
ItemImpl,
71+
}
72+
5773
/// The signatures collected while parsing the macro input
5874
#[derive(Clone)]
5975
pub(crate) struct PubInterfaceSignatures {
@@ -62,6 +78,9 @@ pub(crate) struct PubInterfaceSignatures {
6278

6379
/// The list of exported signatures
6480
pub(crate) signatures: Vec<syn::Signature>,
81+
82+
/// This records what kind of item the macro was applied to
83+
pub(crate) item_type: PubInterfaceItemType,
6584
}
6685

6786
impl syn::parse::Parse for PubInterfaceSignatures {
@@ -94,26 +113,30 @@ impl syn::parse::Parse for PubInterfaceSignatures {
94113
// If none of the options worked, we tell the user that he gave us bad input.
95114
.map_err(|_err| input.error(ParseError::BadInputItem))?;
96115

97-
let mut implementor: syn::Type;
116+
let item_type: PubInterfaceItemType;
117+
let implementor: syn::Type;
98118
let signatures = match pub_interface_input {
99119
PubInterfaceInput::ItemTrait(item_trait) => {
120+
item_type = PubInterfaceItemType::ItemTrait;
100121
let default_implementor_name = super::DEFAULT_IMPLEMENTOR_NAME.into_ident();
101122
implementor = syn::parse2::<syn::Type>(quote!(#default_implementor_name)).unwrap();
102123
get_signatures_from_item_trait(item_trait)
103124
}
104125
PubInterfaceInput::ItemImpl(item_impl) => {
105126
if let Some(trait_) = item_impl.trait_ {
106127
return Err(syn::Error::new_spanned(trait_.1, ParseError::TraitImpl));
107-
} else {
108-
implementor = *item_impl.self_ty.clone();
109-
get_signatures_from_item_impl(item_impl)
110128
}
129+
130+
item_type = PubInterfaceItemType::ItemImpl;
131+
implementor = *item_impl.self_ty.clone();
132+
get_signatures_from_item_impl(item_impl)
111133
}
112134
}?;
113135

114136
Ok(Self {
115137
implementor,
116138
signatures,
139+
item_type,
117140
})
118141
}
119142
}

0 commit comments

Comments
 (0)