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

Commit 5766b3d

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 5766b3d

File tree

3 files changed

+163
-10
lines changed

3 files changed

+163
-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: 133 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,48 @@ 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+
match pub_interface_signatures.item_type {
82+
PubInterfaceItemType::ItemTrait => {
83+
let implementor: syn::Type = syn::parse2(attr)?;
84+
pub_interface_signatures.implementor = implementor;
85+
Ok(())
86+
}
87+
PubInterfaceItemType::ItemImpl if !attr.is_empty() => Err(syn::Error::new_spanned(
88+
attr,
89+
ParseError::CustomImplementorOnImpl,
90+
)),
91+
_ => Ok(()),
92+
}
93+
}
94+
4795
fn generate_eng_wasm_aux_functions(
4896
function_name_func_name: &syn::Ident,
4997
args_func_name: &syn::Ident,
@@ -250,6 +298,88 @@ fn generate_dispatch_function(
250298
mod tests {
251299
use super::*;
252300

301+
#[test]
302+
fn apply_macro_attr_works_on_trait() {
303+
let attr_tokens = quote!(Bar);
304+
305+
let mut pub_interface_signatures = PubInterfaceSignatures {
306+
signatures: vec![],
307+
implementor: syn::parse2(quote!(Foo)).unwrap(),
308+
item_type: PubInterfaceItemType::ItemTrait,
309+
};
310+
311+
apply_macro_attr(attr_tokens.clone(), &mut pub_interface_signatures).unwrap();
312+
313+
assert_eq!(
314+
syn::parse2::<syn::Type>(attr_tokens).unwrap(),
315+
pub_interface_signatures.implementor
316+
);
317+
}
318+
319+
#[test]
320+
fn apply_macro_attr_with_bad_input_fails_on_trait() {
321+
let attr_tokens = quote!(this is not a valid type);
322+
323+
let mut pub_interface_signatures = PubInterfaceSignatures {
324+
signatures: vec![],
325+
implementor: syn::parse2(quote!(Foo)).unwrap(),
326+
item_type: PubInterfaceItemType::ItemTrait,
327+
};
328+
329+
let error =
330+
apply_macro_attr(attr_tokens.clone(), &mut pub_interface_signatures).unwrap_err();
331+
332+
assert_eq!(error.to_string(), "unexpected token");
333+
// Check that the implementor did not change
334+
assert_eq!(
335+
syn::parse2::<syn::Type>(quote!(Foo)).unwrap(),
336+
pub_interface_signatures.implementor
337+
);
338+
}
339+
340+
#[test]
341+
fn apply_macro_attr_with_empty_input_works_on_impl() {
342+
let attr_tokens = quote!(); // This is empty
343+
344+
let mut pub_interface_signatures = PubInterfaceSignatures {
345+
signatures: vec![],
346+
implementor: syn::parse2(quote!(Foo)).unwrap(),
347+
item_type: PubInterfaceItemType::ItemImpl,
348+
};
349+
350+
apply_macro_attr(attr_tokens.clone(), &mut pub_interface_signatures).unwrap();
351+
352+
// Check that the implementor did not change
353+
assert_eq!(
354+
syn::parse2::<syn::Type>(quote!(Foo)).unwrap(),
355+
pub_interface_signatures.implementor
356+
);
357+
}
358+
359+
#[test]
360+
fn apply_macro_attr_fails_on_impl() {
361+
let attr_tokens = quote!(Bar);
362+
363+
let mut pub_interface_signatures = PubInterfaceSignatures {
364+
signatures: vec![],
365+
implementor: syn::parse2(quote!(Foo)).unwrap(),
366+
item_type: PubInterfaceItemType::ItemImpl,
367+
};
368+
369+
let error =
370+
apply_macro_attr(attr_tokens.clone(), &mut pub_interface_signatures).unwrap_err();
371+
372+
assert_eq!(
373+
error.to_string(),
374+
ParseError::CustomImplementorOnImpl.to_string()
375+
);
376+
// Check that the implementor did not change
377+
assert_eq!(
378+
syn::parse2::<syn::Type>(quote!(Foo)).unwrap(),
379+
pub_interface_signatures.implementor
380+
);
381+
}
382+
253383
#[test]
254384
fn deploy_generation() -> syn::Result<()> {
255385
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)