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 32eca8698..09542bd71 100644 --- a/near-sdk-macros/src/core_impl/abi/abi_generator.rs +++ b/near-sdk-macros/src/core_impl/abi/abi_generator.rs @@ -1,6 +1,6 @@ use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote}; -use syn::{Attribute, Lit::Str, Meta::NameValue, MetaNameValue, Type}; +use syn::{parse_quote, Attribute, Lit::Str, Meta::NameValue, MetaNameValue, Type}; use crate::core_impl::{ utils, BindgenArgType, ImplItemMethodInfo, ItemImplInfo, MethodKind, ReturnKind, SerializerType, @@ -202,7 +202,11 @@ impl ImplItemMethodInfo { match &self.attr_signature_info.returns.kind { Default => quote! { None }, General(ty) => self.abi_result_tokens_with_return_value(ty), - HandlesResult { ok_type } => self.abi_result_tokens_with_return_value(ok_type), + HandlesResult { ty } => { + // extract the `Ok` type from the result + let ty = parse_quote! { <#ty as near_sdk::__private::ResultTypeExt>::Okay }; + self.abi_result_tokens_with_return_value(&ty) + } } } @@ -329,7 +333,7 @@ mod tests { callbacks: vec![], callbacks_vec: None, result: Some(near_sdk::__private::AbiType::Json { - type_schema: gen.subschema_for::(), + type_schema: gen.subschema_for::< as near_sdk::__private::ResultTypeExt>::Okay>(), }) } }; @@ -366,7 +370,7 @@ mod tests { callbacks: vec![], callbacks_vec: None, result: Some(near_sdk::__private::AbiType::Borsh { - type_schema: ::schema_container(), + type_schema: < as near_sdk::__private::ResultTypeExt>::Okay as near_sdk::borsh::BorshSchema>::schema_container(), }) } }; 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 aed41846b..ad90d5f1e 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 @@ -62,18 +62,6 @@ mod tests { assert!(matches!(actual, ReturnType::Type(_, ty) if ty.as_ref() == &expected)); } - #[test] - fn handle_result_incorrect_return_type() { - let impl_type: Type = syn::parse_str("Hello").unwrap(); - let mut method: ImplItemMethod = parse_quote! { - #[handle_result] - pub fn method(&self) -> &'static str { } - }; - let actual = ImplItemMethodInfo::new(&mut method, false, impl_type).map(|_| ()).unwrap_err(); - let expected = "Function marked with #[handle_result] should return Result (where E implements FunctionError)."; - assert_eq!(expected, actual.to_string()); - } - #[test] fn handle_result_without_marker() { let impl_type: Type = syn::parse_str("Hello").unwrap(); diff --git a/near-sdk-macros/src/core_impl/info_extractor/mod.rs b/near-sdk-macros/src/core_impl/info_extractor/mod.rs index 622761597..ec64fb98f 100644 --- a/near-sdk-macros/src/core_impl/info_extractor/mod.rs +++ b/near-sdk-macros/src/core_impl/info_extractor/mod.rs @@ -84,5 +84,5 @@ pub struct Returns { pub enum ReturnKind { Default, General(Type), - HandlesResult { ok_type: Type }, + HandlesResult { ty: Type }, } diff --git a/near-sdk-macros/src/core_impl/info_extractor/visitor.rs b/near-sdk-macros/src/core_impl/info_extractor/visitor.rs index 1f94c30bc..b698a8897 100644 --- a/near-sdk-macros/src/core_impl/info_extractor/visitor.rs +++ b/near-sdk-macros/src/core_impl/info_extractor/visitor.rs @@ -209,14 +209,7 @@ fn is_view(sig: &Signature) -> bool { fn parse_return_kind(typ: &Type, handles_result: bool) -> syn::Result { if handles_result { - if let Some(ok_type) = utils::extract_ok_type(typ) { - Ok(ReturnKind::HandlesResult { ok_type: ok_type.clone() }) - } else { - Err(Error::new( - typ.span(), - "Function marked with #[handle_result] should return Result (where E implements FunctionError).", - )) - } + Ok(ReturnKind::HandlesResult { ty: typ.clone() }) } else if utils::type_is_result(typ) { Err(Error::new( typ.span(), diff --git a/near-sdk/compilation_tests/all.rs b/near-sdk/compilation_tests/all.rs index 09f9d73c7..99415e049 100644 --- a/near-sdk/compilation_tests/all.rs +++ b/near-sdk/compilation_tests/all.rs @@ -25,6 +25,7 @@ fn compilation_tests() { t.compile_fail("compilation_tests/generic_function.rs"); t.compile_fail("compilation_tests/generic_const_function.rs"); t.pass("compilation_tests/self_support.rs"); + t.pass("compilation_tests/handle_result_alias.rs"); // The following couple tests should be activated before releasing 5.0 // See: https://github.com/near/near-sdk-rs/issues/1005 // diff --git a/near-sdk/compilation_tests/handle_result_alias.rs b/near-sdk/compilation_tests/handle_result_alias.rs new file mode 100644 index 000000000..193e63c01 --- /dev/null +++ b/near-sdk/compilation_tests/handle_result_alias.rs @@ -0,0 +1,20 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use near_sdk::near_bindgen; + +type MyResult = Result; + +#[near_bindgen] +#[derive(Default, BorshDeserialize, BorshSerialize)] +struct Contract { + value: u32, +} + +#[near_bindgen] +impl Contract { + #[handle_result] + pub fn fun(&self) -> MyResult { + Err("error") + } +} + +fn main() {} diff --git a/near-sdk/src/private/mod.rs b/near-sdk/src/private/mod.rs index 15edef0f1..a174702d3 100644 --- a/near-sdk/src/private/mod.rs +++ b/near-sdk/src/private/mod.rs @@ -12,6 +12,9 @@ pub use schemars; mod metadata; pub use metadata::{Metadata, MethodMetadata}; +mod result_type_ext; +pub use result_type_ext::ResultTypeExt; + use crate::IntoStorageKey; use borsh::BorshSerialize; diff --git a/near-sdk/src/private/result_type_ext.rs b/near-sdk/src/private/result_type_ext.rs new file mode 100644 index 000000000..2739c8e26 --- /dev/null +++ b/near-sdk/src/private/result_type_ext.rs @@ -0,0 +1,17 @@ +pub trait ResultTypeExt: seal::ResultTypeExtSeal { + type Okay; + type Error; +} + +impl ResultTypeExt for Result { + type Okay = T; + type Error = E; +} + +// This is the "sealed trait" pattern: +// https://rust-lang.github.io/api-guidelines/future-proofing.html +mod seal { + pub trait ResultTypeExtSeal {} + + impl ResultTypeExtSeal for Result {} +}