diff --git a/near-sdk-macros/src/core_impl/abi/abi_generator.rs b/near-sdk-macros/src/core_impl/abi/abi_generator.rs index acab4c6f7..7ceb15b5e 100644 --- a/near-sdk-macros/src/core_impl/abi/abi_generator.rs +++ b/near-sdk-macros/src/core_impl/abi/abi_generator.rs @@ -9,15 +9,13 @@ use crate::core_impl::{ }; pub fn generate(i: &ItemImplInfo) -> TokenStream2 { - let public_functions: Vec<&ImplItemMethodInfo> = - i.methods.iter().filter(|m| m.is_public || i.is_trait_impl).collect(); - if public_functions.is_empty() { + if i.methods.is_empty() { // Short-circuit if there are no public functions to export to ABI return TokenStream2::new(); } - let functions: Vec = public_functions.iter().map(|m| m.abi_struct()).collect(); - let first_function_name = &public_functions[0].attr_signature_info.ident; + let functions: Vec = i.methods.iter().map(|m| m.abi_struct()).collect(); + let first_function_name = &i.methods[0].attr_signature_info.ident; let near_abi_symbol = format_ident!("__near_abi_{}", first_function_name); quote! { #[cfg(not(target_arch = "wasm32"))] diff --git a/near-sdk-macros/src/core_impl/code_generator/ext.rs b/near-sdk-macros/src/core_impl/code_generator/ext.rs index 89cde1344..1b653db94 100644 --- a/near-sdk-macros/src/core_impl/code_generator/ext.rs +++ b/near-sdk-macros/src/core_impl/code_generator/ext.rs @@ -241,7 +241,7 @@ mod tests { #[warn(unused)] pub fn method(&self) { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = generate_ext_function(&method_info.attr_signature_info); // Note: only whitelisted non-bindgen attributes are forwarded. @@ -267,7 +267,7 @@ mod tests { let mut method: ImplItemMethod = parse_quote! { pub fn method(&self, k: &String) { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = generate_ext_function(&method_info.attr_signature_info); let expected = quote!( pub fn method(self, k: &String,) -> near_sdk::Promise { @@ -298,7 +298,7 @@ mod tests { let mut method: ImplItemMethod = parse_quote! { pub fn borsh_test(&mut self, #[serializer(borsh)] a: String) {} }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = generate_ext_function(&method_info.attr_signature_info); let expected = quote!( pub fn borsh_test(self, a: String,) -> near_sdk::Promise { diff --git a/near-sdk-macros/src/core_impl/code_generator/item_impl_info.rs b/near-sdk-macros/src/core_impl/code_generator/item_impl_info.rs index aa76c1576..b2c6fefd4 100644 --- a/near-sdk-macros/src/core_impl/code_generator/item_impl_info.rs +++ b/near-sdk-macros/src/core_impl/code_generator/item_impl_info.rs @@ -9,9 +9,7 @@ impl ItemImplInfo { pub fn wrapper_code(&self) -> TokenStream2 { let mut res = TokenStream2::new(); for method in &self.methods { - if method.is_public || self.is_trait_impl { - res.extend(method.method_wrapper()); - } + res.extend(method.method_wrapper()); } res } @@ -20,10 +18,7 @@ impl ItemImplInfo { match syn::parse::(self.ty.to_token_stream().into()) { Ok(n) => generate_ext_function_wrappers( &n, - self.methods - .iter() - .filter(|m| m.is_public || self.is_trait_impl) - .map(|m| &m.attr_signature_info), + self.methods.iter().map(|m| &m.attr_signature_info), ), Err(e) => syn::Error::new(self.ty.span(), e).to_compile_error(), } @@ -42,7 +37,7 @@ mod tests { fn trait_implt() { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("fn method(&self) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, true, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -60,7 +55,7 @@ mod tests { fn no_args_no_return_no_mut() { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(&self) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -78,7 +73,7 @@ mod tests { fn owned_no_args_no_return_no_mut() { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(self) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -97,7 +92,7 @@ mod tests { fn mut_owned_no_args_no_return() { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(mut self) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -115,7 +110,7 @@ mod tests { fn no_args_no_return_mut() { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(&mut self) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -137,7 +132,7 @@ mod tests { fn arg_no_return_no_mut() { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(&self, k: u64) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -165,7 +160,7 @@ mod tests { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(&mut self, k: u64, m: Bar) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -198,7 +193,7 @@ mod tests { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(&mut self, k: u64, m: Bar) -> Option { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -234,7 +229,7 @@ mod tests { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(&self) -> &Option { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -255,7 +250,7 @@ mod tests { fn arg_ref() { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(&self, k: &u64) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -283,7 +278,7 @@ mod tests { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("pub fn method(&self, k: &mut u64) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -312,7 +307,7 @@ mod tests { let mut method: ImplItemMethod = parse_quote! { #[private] pub fn method(&self, #[callback_unwrap] x: &mut u64, y: String, #[callback_unwrap] z: Vec) { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -356,7 +351,7 @@ mod tests { let mut method: ImplItemMethod = parse_quote! { #[private] pub fn method(&self, #[callback_unwrap] x: &mut u64, #[callback_unwrap] y: String) { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -392,7 +387,7 @@ mod tests { let mut method: ImplItemMethod = parse_quote! { #[private] pub fn method(&self, #[callback_result] x: &mut Result, #[callback_result] y: Result) { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -424,7 +419,7 @@ mod tests { let mut method: ImplItemMethod = parse_quote! { #[private] pub fn method(&self, #[callback_vec] x: Vec, y: String) { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -466,7 +461,7 @@ mod tests { #[init] pub fn method(k: &mut u64) -> Self { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -502,7 +497,7 @@ mod tests { #[init] pub fn method(k: &mut u64) { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( compile_error! { "Init methods must return the contract state" } @@ -517,7 +512,7 @@ mod tests { #[init(ignore_state)] pub fn method(k: &mut u64) -> Self { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -551,7 +546,7 @@ mod tests { #[payable] pub fn method(k: &mut u64) -> Self { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -584,7 +579,7 @@ mod tests { #[result_serializer(borsh)] pub fn method(&mut self, #[serializer(borsh)] k: u64, #[serializer(borsh)]m: Bar) -> Option { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -620,7 +615,7 @@ mod tests { let mut method: ImplItemMethod = parse_quote! { #[private] pub fn method(&self, #[callback_unwrap] #[serializer(borsh)] x: &mut u64, #[serializer(borsh)] y: String, #[callback_unwrap] #[serializer(json)] z: Vec) { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -661,7 +656,7 @@ mod tests { fn no_args_no_return_mut_payable() { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("#[payable] pub fn method(&mut self) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -680,7 +675,7 @@ mod tests { fn private_method() { let impl_type: Type = syn::parse_str("Hello").unwrap(); let mut method: ImplItemMethod = syn::parse_str("#[private] pub fn private_method(&mut self) { }").unwrap(); - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -708,7 +703,7 @@ mod tests { #[handle_result] pub fn method(&self) -> Result { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -738,7 +733,7 @@ mod tests { #[result_serializer(borsh)] pub fn method(&self) -> Result { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -768,7 +763,7 @@ mod tests { #[handle_result] pub fn new() -> Result { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -799,7 +794,7 @@ mod tests { #[handle_result] pub fn new() -> Result { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( #[cfg(target_arch = "wasm32")] @@ -826,7 +821,7 @@ mod tests { #[handle_result] pub fn method(&self) -> &'static str { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( compile_error! { @@ -842,7 +837,7 @@ mod tests { let mut method: ImplItemMethod = parse_quote! { pub fn method(&self) -> Result { } }; - let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, false, impl_type).unwrap().unwrap(); let actual = method_info.method_wrapper(); let expected = quote!( compile_error! { diff --git a/near-sdk-macros/src/core_impl/info_extractor/attr_sig_info.rs b/near-sdk-macros/src/core_impl/info_extractor/attr_sig_info.rs index ff05ba382..1a75ef703 100644 --- a/near-sdk-macros/src/core_impl/info_extractor/attr_sig_info.rs +++ b/near-sdk-macros/src/core_impl/info_extractor/attr_sig_info.rs @@ -3,7 +3,7 @@ use crate::core_impl::utils; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::ToTokens; use syn::spanned::Spanned; -use syn::{Attribute, Error, FnArg, Ident, Receiver, ReturnType, Signature}; +use syn::{Attribute, Error, FnArg, GenericParam, Ident, Receiver, ReturnType, Signature}; /// Information extracted from method attributes and signature. pub struct AttrSigInfo { @@ -40,25 +40,28 @@ impl AttrSigInfo { original_sig: &mut Signature, source_type: &TokenStream2, ) -> syn::Result { - if original_sig.asyncness.is_some() { - return Err(Error::new( - original_sig.span(), - "Contract API is not allowed to be async.", - )); - } - if original_sig.abi.is_some() { - return Err(Error::new( - original_sig.span(), - "Contract API is not allowed to have binary interface.", - )); + let mut errors = vec![]; + for generic in &original_sig.generics.params { + match generic { + GenericParam::Type(type_generic) => { + errors.push(Error::new( + type_generic.span(), + "Contract API is not allowed to have generics.", + )); + } + GenericParam::Const(const_generic) => { + // `generic.span()` points to the `const` part of const generics, so we use `ident` explicitly. + errors.push(Error::new( + const_generic.ident.span(), + "Contract API is not allowed to have generics.", + )); + } + _ => {} + } } - if original_sig.variadic.is_some() { - return Err(Error::new( - original_sig.span(), - "Contract API is not allowed to have variadic arguments.", - )); + if let Some(combined_errors) = errors.into_iter().reduce(|mut l, r| (l.combine(r), l).1) { + return Err(combined_errors); } - let ident = original_sig.ident.clone(); let mut non_bindgen_attrs = vec![]; let mut args = vec![]; diff --git a/near-sdk-macros/src/core_impl/info_extractor/impl_item_method_info.rs b/near-sdk-macros/src/core_impl/info_extractor/impl_item_method_info.rs index a0887a440..46dc68059 100644 --- a/near-sdk-macros/src/core_impl/info_extractor/impl_item_method_info.rs +++ b/near-sdk-macros/src/core_impl/info_extractor/impl_item_method_info.rs @@ -1,4 +1,5 @@ use crate::core_impl::info_extractor::AttrSigInfo; +use crate::core_impl::utils; use quote::ToTokens; use syn::{ImplItemMethod, Type, Visibility}; @@ -6,18 +7,24 @@ use syn::{ImplItemMethod, Type, Visibility}; pub struct ImplItemMethodInfo { /// Information on the attributes and the signature of the method. pub attr_signature_info: AttrSigInfo, - /// Whether method has `pub` modifier. - pub is_public: bool, /// The type of the contract struct. pub struct_type: Type, } impl ImplItemMethodInfo { /// Process the method and extract information important for near-sdk. - pub fn new(original: &mut ImplItemMethod, struct_type: Type) -> syn::Result { + pub fn new( + original: &mut ImplItemMethod, + is_trait_impl: bool, + struct_type: Type, + ) -> syn::Result> { let ImplItemMethod { attrs, sig, .. } = original; - let attr_signature_info = AttrSigInfo::new(attrs, sig, &struct_type.to_token_stream())?; - let is_public = matches!(original.vis, Visibility::Public(_)); - Ok(Self { attr_signature_info, is_public, struct_type }) + utils::sig_is_supported(sig)?; + if is_trait_impl || matches!(original.vis, Visibility::Public(_)) { + let attr_signature_info = AttrSigInfo::new(attrs, sig, &struct_type.to_token_stream())?; + Ok(Some(Self { attr_signature_info, struct_type })) + } else { + Ok(None) + } } } diff --git a/near-sdk-macros/src/core_impl/info_extractor/item_impl_info.rs b/near-sdk-macros/src/core_impl/info_extractor/item_impl_info.rs index ec8b1b0e4..785fdf062 100644 --- a/near-sdk-macros/src/core_impl/info_extractor/item_impl_info.rs +++ b/near-sdk-macros/src/core_impl/info_extractor/item_impl_info.rs @@ -4,11 +4,9 @@ use syn::{Error, ImplItem, ItemImpl, Type}; /// Information extracted from `impl` section. pub struct ItemImplInfo { - /// Whether this is a trait implementation. - pub is_trait_impl: bool, /// The type for which this `impl` is written. pub ty: Type, - /// Info extracted for each method. + /// Info extracted for each public method. pub methods: Vec, } @@ -26,10 +24,11 @@ impl ItemImplInfo { let mut methods = vec![]; for subitem in &mut original.items { if let ImplItem::Method(m) = subitem { - let method_info = ImplItemMethodInfo::new(m, ty.clone())?; - methods.push(method_info); + if let Some(method_info) = ImplItemMethodInfo::new(m, is_trait_impl, ty.clone())? { + methods.push(method_info); + } } } - Ok(Self { is_trait_impl, ty, methods }) + Ok(Self { ty, methods }) } } diff --git a/near-sdk-macros/src/core_impl/info_extractor/trait_item_method_info.rs b/near-sdk-macros/src/core_impl/info_extractor/trait_item_method_info.rs index de0d95c1f..bf26494b3 100644 --- a/near-sdk-macros/src/core_impl/info_extractor/trait_item_method_info.rs +++ b/near-sdk-macros/src/core_impl/info_extractor/trait_item_method_info.rs @@ -1,4 +1,5 @@ use super::AttrSigInfo; +use crate::core_impl::utils; use proc_macro2::TokenStream as TokenStream2; use syn::spanned::Spanned; use syn::{Error, LitStr, TraitItemMethod}; @@ -26,6 +27,7 @@ impl TraitItemMethodInfo { let TraitItemMethod { attrs, sig, .. } = original; + utils::sig_is_supported(sig)?; let attr_sig_info = AttrSigInfo::new(attrs, sig, trait_name)?; let ident_byte_str = diff --git a/near-sdk-macros/src/core_impl/utils/mod.rs b/near-sdk-macros/src/core_impl/utils/mod.rs index 8f34478b7..4d70c4203 100644 --- a/near-sdk-macros/src/core_impl/utils/mod.rs +++ b/near-sdk-macros/src/core_impl/utils/mod.rs @@ -1,6 +1,7 @@ use proc_macro2::{Group, TokenStream as TokenStream2, TokenTree}; use quote::quote; -use syn::{GenericArgument, Path, PathArguments, Type}; +use syn::spanned::Spanned; +use syn::{GenericArgument, Path, PathArguments, Signature, Type}; /// Checks whether the given path is literally "Result". /// Note that it won't match a fully qualified name `core::result::Result` or a type alias like @@ -76,6 +77,27 @@ pub(crate) fn extract_vec_type(ty: &Type) -> Option<&Type> { } } +/// Checks that the method signature is supported in the NEAR Contract API. +pub(crate) fn sig_is_supported(sig: &Signature) -> syn::Result<()> { + if sig.asyncness.is_some() { + return Err(syn::Error::new(sig.span(), "Contract API is not allowed to be async.")); + } + if sig.abi.is_some() { + return Err(syn::Error::new( + sig.span(), + "Contract API is not allowed to have binary interface.", + )); + } + if sig.variadic.is_some() { + return Err(syn::Error::new( + sig.span(), + "Contract API is not allowed to have variadic arguments.", + )); + } + + Ok(()) +} + fn _sanitize_self(typ: TokenStream2, replace_with: &TokenStream2) -> TokenStream2 { let trees = typ.into_iter().map(|t| match t { TokenTree::Ident(ident) if ident == "Self" => replace_with diff --git a/near-sdk/compilation_tests/all.rs b/near-sdk/compilation_tests/all.rs index 16a646fb4..d7a5fd0d6 100644 --- a/near-sdk/compilation_tests/all.rs +++ b/near-sdk/compilation_tests/all.rs @@ -22,5 +22,7 @@ fn compilation_tests() { t.pass("compilation_tests/enum_near_bindgen.rs"); t.pass("compilation_tests/schema_derive.rs"); t.compile_fail("compilation_tests/schema_derive_invalids.rs"); + t.compile_fail("compilation_tests/generic_function.rs"); + t.compile_fail("compilation_tests/generic_const_function.rs"); t.pass("compilation_tests/self_support.rs"); } diff --git a/near-sdk/compilation_tests/generic_const_function.rs b/near-sdk/compilation_tests/generic_const_function.rs new file mode 100644 index 000000000..f9e1d4abe --- /dev/null +++ b/near-sdk/compilation_tests/generic_const_function.rs @@ -0,0 +1,19 @@ +//! Functions can't use const generics. + +use borsh::{BorshDeserialize, BorshSerialize}; +use near_sdk::near_bindgen; + +#[near_bindgen] +#[derive(Default, BorshDeserialize, BorshSerialize)] +struct Ident { + value: u32, +} + +#[near_bindgen] +impl Ident { + pub fn is_ident_const(&self, val: [u32; N]) -> [u32; N] { + val + } +} + +fn main() {} diff --git a/near-sdk/compilation_tests/generic_const_function.stderr b/near-sdk/compilation_tests/generic_const_function.stderr new file mode 100644 index 000000000..608bbcd87 --- /dev/null +++ b/near-sdk/compilation_tests/generic_const_function.stderr @@ -0,0 +1,5 @@ +error: Contract API is not allowed to have generics. + --> compilation_tests/generic_const_function.rs:14:33 + | +14 | pub fn is_ident_const(&self, val: [u32; N]) -> [u32; N] { + | ^ diff --git a/near-sdk/compilation_tests/generic_function.rs b/near-sdk/compilation_tests/generic_function.rs new file mode 100644 index 000000000..91e3e1faf --- /dev/null +++ b/near-sdk/compilation_tests/generic_function.rs @@ -0,0 +1,19 @@ +//! Functions can't use generics. + +use borsh::{BorshDeserialize, BorshSerialize}; +use near_sdk::near_bindgen; + +#[near_bindgen] +#[derive(Default, BorshDeserialize, BorshSerialize)] +struct Ident { + value: u32, +} + +#[near_bindgen] +impl Ident { + pub fn is_ident(&self, val: T) -> T { + val + } +} + +fn main() {} diff --git a/near-sdk/compilation_tests/generic_function.stderr b/near-sdk/compilation_tests/generic_function.stderr new file mode 100644 index 000000000..238f56e9d --- /dev/null +++ b/near-sdk/compilation_tests/generic_function.stderr @@ -0,0 +1,5 @@ +error: Contract API is not allowed to have generics. + --> compilation_tests/generic_function.rs:14:21 + | +14 | pub fn is_ident(&self, val: T) -> T { + | ^