From 59824454f839dc0662949bcbfeb680c88d8905dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 30 Mar 2020 01:47:41 +0200 Subject: [PATCH 1/4] Implements mocking of runtime apis This pr adds support for easily mock runtime api implementations for tests by using the `mock_impl_runtime_apis!` macro. The syntax is similar to `impl_runtime_apis!`. The mocked implementation automatically implements `ApiExt`, `ApiErrorExt` and `Core` as these are required by the runtime api traits, but not required in tests or only a subset of them. --- client/authority-discovery/src/tests.rs | 96 +---- client/finality-grandpa/src/tests.rs | 86 +---- .../api/proc-macro/src/decl_runtime_apis.rs | 16 +- .../api/proc-macro/src/impl_runtime_apis.rs | 80 ++-- primitives/api/proc-macro/src/lib.rs | 6 + .../proc-macro/src/mock_impl_runtime_apis.rs | 364 ++++++++++++++++++ primitives/api/proc-macro/src/utils.rs | 90 ++++- primitives/api/src/lib.rs | 87 ++++- primitives/api/test/tests/decl_and_impl.rs | 52 ++- .../ui/mock_only_error_associated_type.rs | 19 + .../ui/mock_only_error_associated_type.stderr | 5 + .../test/tests/ui/mock_only_one_block_type.rs | 27 ++ .../tests/ui/mock_only_one_block_type.stderr | 13 + .../test/tests/ui/mock_only_one_error_type.rs | 29 ++ .../tests/ui/mock_only_one_error_type.stderr | 27 ++ .../test/tests/ui/mock_only_one_self_type.rs | 26 ++ .../tests/ui/mock_only_one_self_type.stderr | 13 + .../test/tests/ui/mock_only_self_reference.rs | 20 + .../tests/ui/mock_only_self_reference.stderr | 65 ++++ 19 files changed, 870 insertions(+), 251 deletions(-) create mode 100644 primitives/api/proc-macro/src/mock_impl_runtime_apis.rs create mode 100644 primitives/api/test/tests/ui/mock_only_error_associated_type.rs create mode 100644 primitives/api/test/tests/ui/mock_only_error_associated_type.stderr create mode 100644 primitives/api/test/tests/ui/mock_only_one_block_type.rs create mode 100644 primitives/api/test/tests/ui/mock_only_one_block_type.stderr create mode 100644 primitives/api/test/tests/ui/mock_only_one_error_type.rs create mode 100644 primitives/api/test/tests/ui/mock_only_one_error_type.stderr create mode 100644 primitives/api/test/tests/ui/mock_only_one_self_type.rs create mode 100644 primitives/api/test/tests/ui/mock_only_one_self_type.stderr create mode 100644 primitives/api/test/tests/ui/mock_only_self_reference.rs create mode 100644 primitives/api/test/tests/ui/mock_only_self_reference.stderr diff --git a/client/authority-discovery/src/tests.rs b/client/authority-discovery/src/tests.rs index 1296c2b627a1e..3178a06d905b2 100644 --- a/client/authority-discovery/src/tests.rs +++ b/client/authority-discovery/src/tests.rs @@ -21,8 +21,8 @@ use futures::executor::block_on; use futures::future::poll_fn; use libp2p::{kad, PeerId}; -use sp_api::{ApiExt, ApiErrorExt, Core, RuntimeVersion, StorageProof, ProvideRuntimeApi, ApiRef}; -use sp_core::{testing::KeyStore, ExecutionContext, NativeOrEncoded}; +use sp_api::{ProvideRuntimeApi, ApiRef}; +use sp_core::testing::KeyStore; use sp_runtime::traits::{Zero, Block as BlockT, NumberFor}; use substrate_test_runtime_client::runtime::Block; @@ -99,8 +99,7 @@ impl ProvideRuntimeApi for TestApi { fn runtime_api<'a>(&'a self) -> ApiRef<'a, Self::Api> { RuntimeApi { authorities: self.authorities.clone(), - } - .into() + }.into() } } @@ -149,90 +148,13 @@ struct RuntimeApi { authorities: Vec, } -impl Core for RuntimeApi { - fn Core_version_runtime_api_impl( - &self, - _: &BlockId, - _: ExecutionContext, - _: Option<()>, - _: Vec, - ) -> std::result::Result, sp_blockchain::Error> { - unimplemented!("Not required for testing!") - } - - fn Core_execute_block_runtime_api_impl( - &self, - _: &BlockId, - _: ExecutionContext, - _: Option, - _: Vec, - ) -> std::result::Result, sp_blockchain::Error> { - unimplemented!("Not required for testing!") - } +sp_api::mock_impl_runtime_apis! { + impl AuthorityDiscoveryApi for RuntimeApi { + type Error = sp_blockchain::Error; - fn Core_initialize_block_runtime_api_impl( - &self, - _: &BlockId, - _: ExecutionContext, - _: Option<&::Header>, - _: Vec, - ) -> std::result::Result, sp_blockchain::Error> { - unimplemented!("Not required for testing!") - } -} - -impl ApiErrorExt for RuntimeApi { - type Error = sp_blockchain::Error; -} - -impl ApiExt for RuntimeApi { - type StateBackend = < - substrate_test_runtime_client::Backend as sc_client_api::backend::Backend - >::State; - - fn map_api_result std::result::Result, R, E>( - &self, - _: F - ) -> std::result::Result { - unimplemented!("Not required for testing!") - } - - fn runtime_version_at( - &self, - _: &BlockId, - ) -> std::result::Result { - unimplemented!("Not required for testing!") - } - - fn record_proof(&mut self) { - unimplemented!("Not required for testing!") - } - - fn extract_proof(&mut self) -> Option { - unimplemented!("Not required for testing!") - } - - fn into_storage_changes( - &self, - _: &Self::StateBackend, - _: Option<&sp_api::ChangesTrieState, sp_api::NumberFor>>, - _: ::Hash, - ) -> std::result::Result, String> - where Self: Sized - { - unimplemented!("Not required for testing!") - } -} - -impl AuthorityDiscoveryApi for RuntimeApi { - fn AuthorityDiscoveryApi_authorities_runtime_api_impl( - &self, - _: &BlockId, - _: ExecutionContext, - _: Option<()>, - _: Vec, - ) -> std::result::Result>, sp_blockchain::Error> { - return Ok(NativeOrEncoded::Native(self.authorities.clone())); + fn authorities(&self) -> Vec { + self.authorities.clone() + } } } diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index 50800f478476f..96bc00ebe4c07 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -214,87 +214,13 @@ impl ProvideRuntimeApi for TestApi { } } -impl Core for RuntimeApi { - fn Core_version_runtime_api_impl( - &self, - _: &BlockId, - _: ExecutionContext, - _: Option<()>, - _: Vec, - ) -> Result> { - unimplemented!("Not required for testing!") - } - - fn Core_execute_block_runtime_api_impl( - &self, - _: &BlockId, - _: ExecutionContext, - _: Option, - _: Vec, - ) -> Result> { - unimplemented!("Not required for testing!") - } - - fn Core_initialize_block_runtime_api_impl( - &self, - _: &BlockId, - _: ExecutionContext, - _: Option<&::Header>, - _: Vec, - ) -> Result> { - unimplemented!("Not required for testing!") - } -} - -impl ApiErrorExt for RuntimeApi { - type Error = sp_blockchain::Error; -} - -impl ApiExt for RuntimeApi { - type StateBackend = < - substrate_test_runtime_client::Backend as sc_client_api::backend::Backend - >::State; - - fn map_api_result result::Result, R, E>( - &self, - _: F - ) -> result::Result { - unimplemented!("Not required for testing!") - } - - fn runtime_version_at(&self, _: &BlockId) -> Result { - unimplemented!("Not required for testing!") - } - - fn record_proof(&mut self) { - unimplemented!("Not required for testing!") - } - - fn extract_proof(&mut self) -> Option { - unimplemented!("Not required for testing!") - } +sp_api::mock_impl_runtime_apis! { + impl GrandpaApi for RuntimeApi { + type Error = sp_blockchain::Error; - fn into_storage_changes( - &self, - _: &Self::StateBackend, - _: Option<&sp_api::ChangesTrieState, sp_api::NumberFor>>, - _: ::Hash, - ) -> std::result::Result, String> - where Self: Sized - { - unimplemented!("Not required for testing!") - } -} - -impl GrandpaApi for RuntimeApi { - fn GrandpaApi_grandpa_authorities_runtime_api_impl( - &self, - _: &BlockId, - _: ExecutionContext, - _: Option<()>, - _: Vec, - ) -> Result> { - Ok(self.inner.genesis_authorities.clone()).map(NativeOrEncoded::Native) + fn grandpa_authorities(&self) -> AuthorityList { + self.inner.genesis_authorities.clone() + } } } diff --git a/primitives/api/proc-macro/src/decl_runtime_apis.rs b/primitives/api/proc-macro/src/decl_runtime_apis.rs index 459707bdb53f4..e9f3087912e26 100644 --- a/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -19,7 +19,7 @@ use crate::utils::{ fold_fn_decl_for_client_side, extract_parameter_names_types_and_borrows, generate_native_call_generator_fn_name, return_type_extract_type, generate_method_runtime_api_impl_name, generate_call_api_at_fn_name, prefix_function_with_trait, - replace_wild_card_parameter_names, + replace_wild_card_parameter_names, AllowSelfRefInParameters, }; use proc_macro2::{TokenStream, Span}; @@ -198,7 +198,7 @@ fn generate_native_call_generators(decl: &ItemTrait) -> Result { // Generate a native call generator for each function of the given trait. for fn_ in fns { - let params = extract_parameter_names_types_and_borrows(&fn_)?; + let params = extract_parameter_names_types_and_borrows(&fn_, AllowSelfRefInParameters::No)?; let trait_fn_name = &fn_.ident; let fn_name = generate_native_call_generator_fn_name(&fn_.ident); let output = return_type_replace_block_with_node_block(fn_.output.clone()); @@ -592,7 +592,10 @@ impl<'a> ToClientSideDecl<'a> { // Get types and if the value is borrowed from all parameters. // If there is an error, we push it as the block to the user. - let param_types = match extract_parameter_names_types_and_borrows(fn_sig) { + let param_types = match extract_parameter_names_types_and_borrows( + fn_sig, + AllowSelfRefInParameters::No, + ) { Ok(res) => res.into_iter().map(|v| { let ty = v.1; let borrow = v.2; @@ -629,7 +632,10 @@ impl<'a> ToClientSideDecl<'a> { mut method: TraitItemMethod, context: TokenStream, ) -> TraitItemMethod { - let params = match extract_parameter_names_types_and_borrows(&method.sig) { + let params = match extract_parameter_names_types_and_borrows( + &method.sig, + AllowSelfRefInParameters::No, + ) { Ok(res) => res.into_iter().map(|v| v.0).collect::>(), Err(e) => { self.errors.push(e.to_compile_error()); @@ -780,7 +786,7 @@ fn generate_runtime_api_id(trait_name: &str) -> TokenStream { let mut res = [0; 8]; res.copy_from_slice(blake2_rfc::blake2b::blake2b(8, &[], trait_name.as_bytes()).as_bytes()); - quote!( const ID: [u8; 8] = [ #( #res ),* ]; ) + quote!( const ID: [u8; 8] = [ #( #res ),* ]; ) } /// Generates the const variable that holds the runtime api version. diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index e16cf3b5c46f7..7def6aa0fb73a 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -19,7 +19,8 @@ use crate::utils::{ generate_runtime_mod_name_for_trait, generate_method_runtime_api_impl_name, extract_parameter_names_types_and_borrows, generate_native_call_generator_fn_name, return_type_extract_type, generate_call_api_at_fn_name, prefix_function_with_trait, - extract_all_signature_types, + extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait, + AllowSelfRefInParameters, RequireQualifiedTraitPath, }; use proc_macro2::{Span, TokenStream}; @@ -66,7 +67,7 @@ fn generate_impl_call( input: &Ident, impl_trait: &Path ) -> Result { - let params = extract_parameter_names_types_and_borrows(signature)?; + let params = extract_parameter_names_types_and_borrows(signature, AllowSelfRefInParameters::No)?; let c = generate_crate_access(HIDDEN_INCLUDES_ID); let c_iter = iter::repeat(&c); @@ -93,50 +94,6 @@ fn generate_impl_call( ) } -/// Extract the trait that is implemented in the given `ItemImpl`. -fn extract_impl_trait<'a>(impl_: &'a ItemImpl) -> Result<&'a Path> { - impl_.trait_.as_ref().map(|v| &v.1).ok_or_else( - || Error::new(impl_.span(), "Only implementation of traits are supported!") - ).and_then(|p| { - if p.segments.len() > 1 { - Ok(p) - } else { - Err( - Error::new( - p.span(), - "The implemented trait has to be referenced with a path, \ - e.g. `impl client::Core for Runtime`." - ) - ) - } - }) -} - -/// Extracts the runtime block identifier. -fn extract_runtime_block_ident(trait_: &Path) -> Result<&TypePath> { - let span = trait_.span(); - let generics = trait_ - .segments - .last() - .ok_or_else(|| Error::new(span, "Empty path not supported"))?; - - match &generics.arguments { - PathArguments::AngleBracketed(ref args) => { - args.args.first().and_then(|v| match v { - GenericArgument::Type(Type::Path(ref block)) => Some(block), - _ => None - }).ok_or_else(|| Error::new(args.span(), "Missing `Block` generic parameter.")) - }, - PathArguments::None => { - let span = trait_.segments.last().as_ref().unwrap().span(); - Err(Error::new(span, "Missing `Block` generic parameter.")) - }, - PathArguments::Parenthesized(_) => { - Err(Error::new(generics.arguments.span(), "Unexpected parentheses in path!")) - } - } -} - /// Generate all the implementation calls for the given functions. fn generate_impl_calls( impls: &[ItemImpl], @@ -145,7 +102,7 @@ fn generate_impl_calls( let mut impl_calls = Vec::new(); for impl_ in impls { - let impl_trait_path = extract_impl_trait(impl_)?; + let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?; let impl_trait = extend_with_runtime_decl_path(impl_trait_path.clone()); let impl_trait_ident = &impl_trait_path .segments @@ -307,11 +264,19 @@ fn generate_runtime_api_base_structures() -> Result { res } - fn runtime_version_at( + fn has_api( + &self, + at: &#crate_::BlockId, + ) -> std::result::Result where Self: Sized { + self.call.runtime_version_at(at).map(|v| v.has_api_with(&A::ID, |v| v == A::VERSION)) + } + + fn has_api_with bool>( &self, at: &#crate_::BlockId, - ) -> std::result::Result<#crate_::RuntimeVersion, C::Error> { - self.call.runtime_version_at(at) + pred: P, + ) -> std::result::Result where Self: Sized { + self.call.runtime_version_at(at).map(|v| v.has_api_with(&A::ID, pred)) } fn record_proof(&mut self) { @@ -450,7 +415,7 @@ fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result { // we put the `RuntimeBlock` as first argument for the trait generics. for impl_ in impls.iter() { let mut impl_ = impl_.clone(); - let trait_ = extract_impl_trait(&impl_)?.clone(); + let trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone(); let trait_ = extend_with_runtime_decl_path(trait_); impl_.trait_.as_mut().unwrap().1 = trait_; @@ -506,7 +471,10 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> { }).collect::>() }; - let (param_types, error) = match extract_parameter_names_types_and_borrows(&input.sig) { + let (param_types, error) = match extract_parameter_names_types_and_borrows( + &input.sig, + AllowSelfRefInParameters::No, + ) { Ok(res) => ( res.into_iter().map(|v| { let ty = v.1; @@ -645,13 +613,13 @@ fn generate_api_impl_for_runtime_api(impls: &[ItemImpl]) -> Result let mut result = Vec::with_capacity(impls.len()); for impl_ in impls { - let impl_trait_path = extract_impl_trait(&impl_)?; + let impl_trait_path = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?; let impl_trait = &impl_trait_path .segments .last() .ok_or_else(|| Error::new(impl_trait_path.span(), "Empty trait path not possible!"))? .clone(); - let runtime_block = extract_runtime_block_ident(impl_trait_path)?; + let runtime_block = extract_block_type_from_trait_path(impl_trait_path)?; let runtime_type = &impl_.self_ty; let mut runtime_mod_path = extend_with_runtime_decl_path(impl_trait_path.clone()); // remove the trait to get just the module path @@ -682,7 +650,9 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { let mut processed_traits = HashSet::new(); for impl_ in impls { - let mut path = extend_with_runtime_decl_path(extract_impl_trait(&impl_)?.clone()); + let mut path = extend_with_runtime_decl_path( + extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone(), + ); // Remove the trait let trait_ = path .segments diff --git a/primitives/api/proc-macro/src/lib.rs b/primitives/api/proc-macro/src/lib.rs index 9e5908717c189..12f435bd166df 100644 --- a/primitives/api/proc-macro/src/lib.rs +++ b/primitives/api/proc-macro/src/lib.rs @@ -21,6 +21,7 @@ use proc_macro::TokenStream; mod impl_runtime_apis; +mod mock_impl_runtime_apis; mod decl_runtime_apis; mod utils; @@ -29,6 +30,11 @@ pub fn impl_runtime_apis(input: TokenStream) -> TokenStream { impl_runtime_apis::impl_runtime_apis_impl(input) } +#[proc_macro] +pub fn mock_impl_runtime_apis(input: TokenStream) -> TokenStream { + mock_impl_runtime_apis::mock_impl_runtime_apis_impl(input) +} + #[proc_macro] pub fn decl_runtime_apis(input: TokenStream) -> TokenStream { decl_runtime_apis::decl_runtime_apis_impl(input) diff --git a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs new file mode 100644 index 0000000000000..6348a6305f6db --- /dev/null +++ b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs @@ -0,0 +1,364 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use crate::utils::{ + generate_crate_access, generate_hidden_includes, + generate_method_runtime_api_impl_name, extract_parameter_names_types_and_borrows, + return_type_extract_type, extract_block_type_from_trait_path, extract_impl_trait, + AllowSelfRefInParameters, RequireQualifiedTraitPath, +}; + +use proc_macro2::{Span, TokenStream}; + +use quote::quote; + +use syn::{ + spanned::Spanned, parse_macro_input, Ident, Type, ItemImpl, ImplItem, TypePath, parse_quote, + parse::{Parse, ParseStream, Result, Error}, fold::{self, Fold}, +}; + +/// Unique identifier used to make the hidden includes unique for this macro. +const HIDDEN_INCLUDES_ID: &str = "MOCK_IMPL_RUNTIME_APIS"; + +/// The structure used for parsing the runtime api implementations. +struct RuntimeApiImpls { + impls: Vec, +} + +impl Parse for RuntimeApiImpls { + fn parse(input: ParseStream) -> Result { + let mut impls = Vec::new(); + + while !input.is_empty() { + impls.push(ItemImpl::parse(input)?); + } + + if impls.is_empty() { + Err(Error::new(Span::call_site(), "No api implementation given!")) + } else { + Ok(Self { impls }) + } + } +} + +/// Implement the `ApiExt` trait, `ApiErrorExt` trait and the `Core` runtime api. +fn implement_common_api_traits( + error_type: Option, + block_type: TypePath, + self_ty: Type, +) -> Result { + let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + + let error_type = error_type.map(|e| quote!(#e)).unwrap_or_else(|| quote!(String)); + + Ok(quote!( + impl #crate_::ApiErrorExt for #self_ty { + type Error = #error_type; + } + + impl #crate_::ApiExt<#block_type> for #self_ty { + type StateBackend = #crate_::InMemoryBackend<#crate_::HashFor<#block_type>>; + + fn map_api_result std::result::Result, R, E>( + &self, + map_call: F, + ) -> std::result::Result where Self: Sized { + map_call(self) + } + + fn has_api( + &self, + _: &#crate_::BlockId<#block_type>, + ) -> std::result::Result where Self: Sized { + Ok(true) + } + + fn has_api_with bool>( + &self, + at: &#crate_::BlockId<#block_type>, + pred: P, + ) -> std::result::Result where Self: Sized { + Ok(pred(A::VERSION)) + } + + fn record_proof(&mut self) { + unimplemented!("`record_proof` not implemented for runtime api mocks") + } + + fn extract_proof(&mut self) -> Option<#crate_::StorageProof> { + unimplemented!("`extract_proof` not implemented for runtime api mocks") + } + + fn into_storage_changes( + &self, + _: &Self::StateBackend, + _: Option<&#crate_::ChangesTrieState< + #crate_::HashFor<#block_type>, + #crate_::NumberFor<#block_type>, + >>, + _: <#block_type as #crate_::BlockT>::Hash, + ) -> std::result::Result< + #crate_::StorageChanges, + String + > where Self: Sized { + unimplemented!("`into_storage_changes` not implemented for runtime api mocks") + } + } + + impl #crate_::Core<#block_type> for #self_ty { + fn Core_version_runtime_api_impl( + &self, + _: &#crate_::BlockId<#block_type>, + _: #crate_::ExecutionContext, + _: Option<()>, + _: Vec, + ) -> std::result::Result<#crate_::NativeOrEncoded<#crate_::RuntimeVersion>, #error_type> { + unimplemented!("Not required for testing!") + } + + fn Core_execute_block_runtime_api_impl( + &self, + _: &#crate_::BlockId<#block_type>, + _: #crate_::ExecutionContext, + _: Option<#block_type>, + _: Vec, + ) -> std::result::Result<#crate_::NativeOrEncoded<()>, #error_type> { + unimplemented!("Not required for testing!") + } + + fn Core_initialize_block_runtime_api_impl( + &self, + _: &#crate_::BlockId<#block_type>, + _: #crate_::ExecutionContext, + _: Option<&<#block_type as #crate_::BlockT>::Header>, + _: Vec, + ) -> std::result::Result<#crate_::NativeOrEncoded<()>, #error_type> { + unimplemented!("Not required for testing!") + } + } + )) +} + +/// Auxialiry structure to fold a runtime api trait implementation into the expected format. +/// +/// This renames the methods, changes the method parameters and extracts the error type. +struct FoldRuntimeApiImpl<'a> { + /// The block type that is being used. + block_type: &'a TypePath, + /// The identifier of the trait being implemented. + impl_trait: &'a Ident, + /// Stores the error type that is being found in the trait implementation as associated type + /// with the name `Error`. + error_type: &'a mut Option, +} + +impl<'a> Fold for FoldRuntimeApiImpl<'a> { + fn fold_impl_item_method(&mut self, mut input: syn::ImplItemMethod) -> syn::ImplItemMethod { + let block = { + let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID); + + let (param_names, param_types, error) = match extract_parameter_names_types_and_borrows( + &input.sig, + AllowSelfRefInParameters::YesButIgnore, + ) { + Ok(res) => ( + res.iter().map(|v| v.0.clone()).collect::>(), + res.iter().map(|v| { + let ty = &v.1; + let borrow = &v.2; + quote!( #borrow #ty ) + }).collect::>(), + None + ), + Err(e) => (Vec::new(), Vec::new(), Some(e.to_compile_error())), + }; + + let block_type = &self.block_type; + + // Rewrite the input parameters. + input.sig.inputs = parse_quote! { + &self, + _: &#crate_::BlockId<#block_type>, + _: #crate_::ExecutionContext, + params: Option<( #( #param_types ),* )>, + _: Vec, + }; + + input.sig.ident = generate_method_runtime_api_impl_name( + &self.impl_trait, + &input.sig.ident, + ); + let ret_type = return_type_extract_type(&input.sig.output); + + // Generate the correct return type. + input.sig.output = parse_quote!( + -> std::result::Result<#crate_::NativeOrEncoded<#ret_type>, Self::Error> + ); + + let orig_block = input.block.clone(); + + // Generate the new method implementation that calls into the runtime. + parse_quote!( + { + // Get the error to the user (if we have one). + #error + + let (#( #param_names ),*) = params + .expect("Mocked runtime apis don't support calling deprecated api versions"); + + let __fn_implementation__ = move || #orig_block; + + Ok(#crate_::NativeOrEncoded::Native(__fn_implementation__())) + } + ) + }; + + let mut input = fold::fold_impl_item_method(self, input); + // We need to set the block, after we modified the rest of the ast, otherwise we would + // modify our generated block as well. + input.block = block; + input + } + + fn fold_impl_item(&mut self, input: ImplItem) -> ImplItem { + match input { + ImplItem::Type(ty) => { + if ty.ident == "Error" { + if let Some(error_type) = self.error_type { + if *error_type != ty.ty { + let error = Error::new( + ty.span(), + "Error type can not change between runtime apis", + ); + ImplItem::Verbatim(error.to_compile_error()) + } else { + ImplItem::Verbatim(Default::default()) + } + } else { + *self.error_type = Some(ty.ty); + ImplItem::Verbatim(Default::default()) + } + } else { + let error = Error::new( + ty.span(), + "Only associated type with name `Error` is allowed", + ); + ImplItem::Verbatim(error.to_compile_error()) + } + }, + o => fold::fold_impl_item(self, o), + } + } +} + +/// Result of [`generate_runtime_api_impls`]. +struct GeneratedRuntimeApiImpls { + /// All the runtime api implementations. + impls: TokenStream, + /// The error type that should be used by the runtime apis. + error_type: Option, + /// The block type that is being used by the runtime apis. + block_type: TypePath, + /// The type the traits are implemented for. + self_ty: Type, +} + +/// Generate the runtime api implementations from the given trait implementations. +/// +/// This folds the method names, changes the method parameters, method return type, +/// extracts the error type, self type and the block type. +fn generate_runtime_api_impls(impls: &[ItemImpl]) -> Result { + let mut result = Vec::with_capacity(impls.len()); + let mut error_type = None; + let mut global_block_type = None; + let mut self_ty = None; + + for impl_ in impls { + let impl_trait_path = extract_impl_trait(&impl_, RequireQualifiedTraitPath::No)?; + let impl_trait = &impl_trait_path + .segments + .last() + .ok_or_else(|| Error::new(impl_trait_path.span(), "Empty trait path not possible!"))? + .clone(); + let block_type = extract_block_type_from_trait_path(impl_trait_path)?; + + self_ty = match self_ty.take() { + Some(self_ty) => { + if self_ty == impl_.self_ty { + Some(self_ty) + } else { + return Err(Error::new( + impl_.self_ty.span(), + "Self type should not change between runtime apis", + )) + } + }, + None => Some(impl_.self_ty.clone()), + }; + + global_block_type = match global_block_type.take() { + Some(global_block_type) => { + if global_block_type == *block_type { + Some(global_block_type) + } else { + return Err(Error::new( + block_type.span(), + "Block type should be the same between all runtime apis.", + )) + } + }, + None => Some(block_type.clone()), + }; + + let mut visitor = FoldRuntimeApiImpl { + block_type, + impl_trait: &impl_trait.ident, + error_type: &mut error_type, + }; + + result.push(visitor.fold_item_impl(impl_.clone())); + } + + Ok(GeneratedRuntimeApiImpls { + impls: quote!( #( #result )* ), + error_type, + block_type: global_block_type.expect("There is a least one runtime api; qed"), + self_ty: *self_ty.expect("There is at least one runtime api; qed"), + }) +} + +/// The implementation of the `mock_impl_runtime_apis!` macro. +pub fn mock_impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + // Parse all impl blocks + let RuntimeApiImpls { impls: api_impls } = parse_macro_input!(input as RuntimeApiImpls); + + mock_impl_runtime_apis_impl_inner(&api_impls).unwrap_or_else(|e| e.to_compile_error()).into() +} + +fn mock_impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { + let hidden_includes = generate_hidden_includes(HIDDEN_INCLUDES_ID); + let GeneratedRuntimeApiImpls { impls, error_type, block_type, self_ty } = + generate_runtime_api_impls(api_impls)?; + let api_traits = implement_common_api_traits(error_type, block_type, self_ty)?; + + Ok(quote!( + #hidden_includes + + #impls + + #api_traits + )) +} diff --git a/primitives/api/proc-macro/src/utils.rs b/primitives/api/proc-macro/src/utils.rs index 8330624bf26d7..90d7ab061d4d5 100644 --- a/primitives/api/proc-macro/src/utils.rs +++ b/primitives/api/proc-macro/src/utils.rs @@ -18,7 +18,7 @@ use proc_macro2::{TokenStream, Span}; use syn::{ Result, Ident, Signature, parse_quote, Type, Pat, spanned::Spanned, FnArg, Error, token::And, - ImplItem, ReturnType, + ImplItem, ReturnType, PathArguments, Path, GenericArgument, TypePath, ItemImpl, }; use quote::quote; @@ -126,13 +126,21 @@ pub fn generate_unique_pattern(pat: Pat, counter: &mut u32) -> Pat { }, _ => pat, } - } +} + +/// Allow `&self` in parameters of a method. +pub enum AllowSelfRefInParameters { + /// Allows `&self` in parameters, but doesn't return it as part of the parameters. + YesButIgnore, + No, +} /// Extracts the name, the type and `&` or ``(if it is a reference or not) /// for each parameter in the given function signature. -pub fn extract_parameter_names_types_and_borrows(sig: &Signature) - -> Result)>> -{ +pub fn extract_parameter_names_types_and_borrows( + sig: &Signature, + allow_self: AllowSelfRefInParameters, +) -> Result)>> { let mut result = Vec::new(); let mut generated_pattern_counter = 0; for input in sig.inputs.iter() { @@ -145,13 +153,20 @@ pub fn extract_parameter_names_types_and_borrows(sig: &Signature) t => { (t.clone(), None) }, }; - let name = - generate_unique_pattern((*arg.pat).clone(), &mut generated_pattern_counter); + let name = generate_unique_pattern( + (*arg.pat).clone(), + &mut generated_pattern_counter, + ); result.push((name, ty, borrow)); }, - FnArg::Receiver(_) => { + FnArg::Receiver(_) if matches!(allow_self, AllowSelfRefInParameters::No) => { return Err(Error::new(input.span(), "`self` parameter not supported!")) - } + }, + FnArg::Receiver(recv) => { + if recv.mutability.is_some() || recv.reference.is_none() { + return Err(Error::new(recv.span(), "Only `&self` is supported!")) + } + }, } } @@ -199,3 +214,60 @@ pub fn extract_all_signature_types(items: &[ImplItem]) -> Vec { .flatten() .collect() } + +/// Extracts the block type from a trait path. +/// +/// It is expected that the block type is the first type in the generic arguments. +pub fn extract_block_type_from_trait_path(trait_: &Path) -> Result<&TypePath> { + let span = trait_.span(); + let generics = trait_ + .segments + .last() + .ok_or_else(|| Error::new(span, "Empty path not supported"))?; + + match &generics.arguments { + PathArguments::AngleBracketed(ref args) => { + args.args.first().and_then(|v| match v { + GenericArgument::Type(Type::Path(ref block)) => Some(block), + _ => None + }).ok_or_else(|| Error::new(args.span(), "Missing `Block` generic parameter.")) + }, + PathArguments::None => { + let span = trait_.segments.last().as_ref().unwrap().span(); + Err(Error::new(span, "Missing `Block` generic parameter.")) + }, + PathArguments::Parenthesized(_) => { + Err(Error::new(generics.arguments.span(), "Unexpected parentheses in path!")) + } + } +} + +/// Should a qualified trait path be required? +/// +/// e.g. `path::Trait` is qualified and `Trait` is not. +pub enum RequireQualifiedTraitPath { + Yes, + No, +} + +/// Extract the trait that is implemented by the given `ItemImpl`. +pub fn extract_impl_trait<'a>( + impl_: &'a ItemImpl, + require: RequireQualifiedTraitPath, +) -> Result<&'a Path> { + impl_.trait_.as_ref().map(|v| &v.1).ok_or_else( + || Error::new(impl_.span(), "Only implementation of traits are supported!") + ).and_then(|p| { + if p.segments.len() > 1 || matches!(require, RequireQualifiedTraitPath::No) { + Ok(p) + } else { + Err( + Error::new( + p.span(), + "The implemented trait has to be referenced with a path, \ + e.g. `impl client::Core for Runtime`." + ) + ) + } + }) +} diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index 0901be5831dfd..74bcf19a9949e 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -36,7 +36,7 @@ extern crate self as sp_api; #[doc(hidden)] #[cfg(feature = "std")] pub use sp_state_machine::{ - OverlayedChanges, StorageProof, Backend as StateBackend, ChangesTrieState, + OverlayedChanges, StorageProof, Backend as StateBackend, ChangesTrieState, InMemoryBackend, }; #[doc(hidden)] #[cfg(feature = "std")] @@ -78,7 +78,8 @@ use std::{panic::UnwindSafe, cell::RefCell}; /// declaration. Besides one exception, the macro adds an extra generic parameter `Block: BlockT` /// to the client side and the runtime side. This generic parameter is usable by the user. /// -/// For implementing these macros you should use the `impl_runtime_apis!` macro. +/// For implementing these macros you should use the +/// [`impl_runtime_apis!`](macro.impl_runtime_apis.html) macro. /// /// # Example /// @@ -143,8 +144,9 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// Tags given trait implementations as runtime apis. /// -/// All traits given to this macro, need to be declared with the `decl_runtime_apis!` macro. -/// The implementation of the trait should follow the declaration given to the `decl_runtime_apis!` +/// All traits given to this macro, need to be declared with the +/// [`decl_runtime_apis!`](macro.decl_runtime_apis.html) macro. The implementation of the trait +/// should follow the declaration given to the [`decl_runtime_apis!`](macro.decl_runtime_apis.html) /// macro, besides the `Block` type that is required as first generic parameter for each runtime /// api trait. When implementing a runtime api trait, it is required that the trait is referenced /// by a path, e.g. `impl my_trait::MyTrait for Runtime`. The macro will use this path to access @@ -182,7 +184,7 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// # } /// # pub trait BlockBuilder { /// # fn build_block() -> Block; -/// # } +/// # } /// # } /// /// /// All runtime api implementations need to be done in one call of the macro! @@ -226,6 +228,70 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// ``` pub use sp_api_proc_macro::impl_runtime_apis; +/// Mocks given trait implementations as runtime apis. +/// +/// Accepts similar syntax as [`impl_runtime_apis!`](macro.impl_runtime_apis.html) and generates +/// simplified mock implementations of the given runtime apis. The difference in syntax is that the +/// trait does not need to be referenced by a qualified path, methods accept the `&self` parameter +/// and the error type can be specified as associated type. If no error type is specified `String` +/// is used as error type. +/// +/// Besides implementing the given traits, the [`Core`], [`ApiExt`] and [`ApiErrorExt`] are +/// implemented automatically. +/// +/// # Example +/// +/// ```rust +/// use sp_version::create_runtime_str; +/// # +/// # use sp_runtime::traits::Block as BlockT; +/// # use sp_test_primitives::Block; +/// # +/// # sp_api::decl_runtime_apis! { +/// # /// Declare the api trait. +/// # pub trait Balance { +/// # /// Get the balance. +/// # fn get_balance() -> u64; +/// # /// Set the balance. +/// # fn set_balance(val: u64); +/// # } +/// # pub trait BlockBuilder { +/// # fn build_block() -> Block; +/// # } +/// # } +/// +/// struct MockApi { +/// balance: u64, +/// } +/// +/// /// All runtime api mock implementations need to be done in one call of the macro! +/// sp_api::mock_impl_runtime_apis! { +/// impl Balance for MockApi { +/// /// Here we take the `&self` to access the instance. +/// fn get_balance(&self) -> u64 { +/// self.balance +/// } +/// fn set_balance(_bal: u64) { +/// // Store the balance +/// } +/// } +/// +/// impl BlockBuilder for MockApi { +/// /// Sets the error type that is being used by the mock implementation. +/// /// The error type is used by all runtime apis. It is only required to +/// /// be specified in one trait implementation. +/// type Error = String; +/// +/// fn build_block() -> Block { +/// unimplemented!("Not Required in tests") +/// } +/// } +/// } +/// +/// # fn main() {} +/// ``` +pub use sp_api_proc_macro::mock_impl_runtime_apis; + /// A type that records all accessed trie nodes and generates a proof out of it. #[cfg(feature = "std")] pub type ProofRecorder = sp_state_machine::ProofRecorder>; @@ -293,21 +359,14 @@ pub trait ApiExt: ApiErrorExt { fn has_api( &self, at: &BlockId, - ) -> Result where Self: Sized { - self.runtime_version_at(at).map(|v| v.has_api_with(&A::ID, |v| v == A::VERSION)) - } + ) -> Result where Self: Sized; /// Check if the given api is implemented and the version passes a predicate. fn has_api_with bool>( &self, at: &BlockId, pred: P, - ) -> Result where Self: Sized { - self.runtime_version_at(at).map(|v| v.has_api_with(&A::ID, pred)) - } - - /// Returns the runtime version at the given block id. - fn runtime_version_at(&self, at: &BlockId) -> Result; + ) -> Result where Self: Sized; /// Start recording all accessed trie nodes for generating proofs. fn record_proof(&mut self); diff --git a/primitives/api/test/tests/decl_and_impl.rs b/primitives/api/test/tests/decl_and_impl.rs index d5a668dec1245..a09bd0412c8af 100644 --- a/primitives/api/test/tests/decl_and_impl.rs +++ b/primitives/api/test/tests/decl_and_impl.rs @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use sp_api::{RuntimeApiInfo, decl_runtime_apis, impl_runtime_apis}; +use sp_api::{ + RuntimeApiInfo, decl_runtime_apis, impl_runtime_apis, mock_impl_runtime_apis, + ApiExt, +}; use sp_runtime::{traits::{GetNodeBlockType, Block as BlockT}, generic::BlockId}; @@ -81,6 +84,34 @@ impl_runtime_apis! { } } +struct MockApi { + block: Option, +} + +mock_impl_runtime_apis! { + impl Api for MockApi { + fn test(_: u64) { + unimplemented!() + } + + fn something_with_block(&self, _: Block) -> Block { + self.block.clone().unwrap() + } + + fn function_with_two_args(_: u64, _: Block) { + unimplemented!() + } + + fn same_name() {} + + fn wild_card(_: u32) {} + } + + impl ApiWithCustomVersion for MockApi { + fn same_name() {} + } +} + type TestClient = substrate_test_runtime_client::sc_client::Client< substrate_test_runtime_client::Backend, substrate_test_runtime_client::Executor, @@ -129,3 +160,22 @@ fn check_runtime_api_versions() { check_runtime_api_versions_contains::>(); check_runtime_api_versions_contains::>(); } + +#[test] +fn mock_runtime_api_has_api() { + let mock = MockApi { block: None }; + + assert!( + mock.has_api::>(&BlockId::Number(0)).unwrap(), + ); + assert!(mock.has_api::>(&BlockId::Number(0)).unwrap()); +} + +#[test] +#[should_panic(expected = "Mocked runtime apis don't support calling deprecated api versions")] +fn mock_runtime_api_panics_on_calling_old_version() { + let mock = MockApi { block: None }; + + #[allow(deprecated)] + let _ = mock.same_name_before_version_2(&BlockId::Number(0)); +} diff --git a/primitives/api/test/tests/ui/mock_only_error_associated_type.rs b/primitives/api/test/tests/ui/mock_only_error_associated_type.rs new file mode 100644 index 0000000000000..bbd3c71c94017 --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_error_associated_type.rs @@ -0,0 +1,19 @@ +use substrate_test_runtime_client::runtime::Block; + +sp_api::decl_runtime_apis! { + pub trait Api { + fn test(data: u64); + } +} + +struct MockApi; + +sp_api::mock_impl_runtime_apis! { + impl Api for MockApi { + type OtherData = u32; + + fn test(data: u64) {} + } +} + +fn main() {} diff --git a/primitives/api/test/tests/ui/mock_only_error_associated_type.stderr b/primitives/api/test/tests/ui/mock_only_error_associated_type.stderr new file mode 100644 index 0000000000000..beced70413bb0 --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_error_associated_type.stderr @@ -0,0 +1,5 @@ +error: Only associated type with name `Error` is allowed + --> $DIR/mock_only_error_associated_type.rs:13:3 + | +13 | type OtherData = u32; + | ^^^^ diff --git a/primitives/api/test/tests/ui/mock_only_one_block_type.rs b/primitives/api/test/tests/ui/mock_only_one_block_type.rs new file mode 100644 index 0000000000000..969b21d7378d7 --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_one_block_type.rs @@ -0,0 +1,27 @@ +use substrate_test_runtime_client::runtime::Block; + +struct Block2; + +sp_api::decl_runtime_apis! { + pub trait Api { + fn test(data: u64); + } + + pub trait Api2 { + fn test(data: u64); + } +} + +struct MockApi; + +sp_api::mock_impl_runtime_apis! { + impl Api for MockApi { + fn test(data: u64) {} + } + + impl Api2 for MockApi { + fn test(data: u64) {} + } +} + +fn main() {} diff --git a/primitives/api/test/tests/ui/mock_only_one_block_type.stderr b/primitives/api/test/tests/ui/mock_only_one_block_type.stderr new file mode 100644 index 0000000000000..6330807ba1c63 --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_one_block_type.stderr @@ -0,0 +1,13 @@ +error: Block type should be the same between all runtime apis. + --> $DIR/mock_only_one_block_type.rs:22:12 + | +22 | impl Api2 for MockApi { + | ^^^^^^ + +warning: unused import: `substrate_test_runtime_client::runtime::Block` + --> $DIR/mock_only_one_block_type.rs:1:5 + | +1 | use substrate_test_runtime_client::runtime::Block; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_imports)]` on by default diff --git a/primitives/api/test/tests/ui/mock_only_one_error_type.rs b/primitives/api/test/tests/ui/mock_only_one_error_type.rs new file mode 100644 index 0000000000000..1c3f13dbb9bf1 --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_one_error_type.rs @@ -0,0 +1,29 @@ +use substrate_test_runtime_client::runtime::Block; + +sp_api::decl_runtime_apis! { + pub trait Api { + fn test(data: u64); + } + + pub trait Api2 { + fn test(data: u64); + } +} + +struct MockApi; + +sp_api::mock_impl_runtime_apis! { + impl Api for MockApi { + type Error = u32; + + fn test(data: u64) {} + } + + impl Api2 for MockApi { + type Error = u64; + + fn test(data: u64) {} + } +} + +fn main() {} diff --git a/primitives/api/test/tests/ui/mock_only_one_error_type.stderr b/primitives/api/test/tests/ui/mock_only_one_error_type.stderr new file mode 100644 index 0000000000000..8f026838c96b8 --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_one_error_type.stderr @@ -0,0 +1,27 @@ +error: Error type can not change between runtime apis + --> $DIR/mock_only_one_error_type.rs:23:3 + | +23 | type Error = u64; + | ^^^^ + +error[E0277]: the trait bound `u32: std::convert::From` is not satisfied + --> $DIR/mock_only_one_error_type.rs:15:1 + | +15 | / sp_api::mock_impl_runtime_apis! { +16 | | impl Api for MockApi { +17 | | type Error = u32; +18 | | +... | +26 | | } +27 | | } + | | ^ + | | | + | |_the trait `std::convert::From` is not implemented for `u32` + | in this macro invocation + | + = help: the following implementations were found: + > + > + > + > + and 16 others diff --git a/primitives/api/test/tests/ui/mock_only_one_self_type.rs b/primitives/api/test/tests/ui/mock_only_one_self_type.rs new file mode 100644 index 0000000000000..4b29ec2a6ab07 --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_one_self_type.rs @@ -0,0 +1,26 @@ +use substrate_test_runtime_client::runtime::Block; + +sp_api::decl_runtime_apis! { + pub trait Api { + fn test(data: u64); + } + + pub trait Api2 { + fn test(data: u64); + } +} + +struct MockApi; +struct MockApi2; + +sp_api::mock_impl_runtime_apis! { + impl Api for MockApi { + fn test(data: u64) {} + } + + impl Api2 for MockApi2 { + fn test(data: u64) {} + } +} + +fn main() {} diff --git a/primitives/api/test/tests/ui/mock_only_one_self_type.stderr b/primitives/api/test/tests/ui/mock_only_one_self_type.stderr new file mode 100644 index 0000000000000..7ef1b2eb0b220 --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_one_self_type.stderr @@ -0,0 +1,13 @@ +error: Self type should not change between runtime apis + --> $DIR/mock_only_one_self_type.rs:21:23 + | +21 | impl Api2 for MockApi2 { + | ^^^^^^^^ + +warning: unused import: `substrate_test_runtime_client::runtime::Block` + --> $DIR/mock_only_one_self_type.rs:1:5 + | +1 | use substrate_test_runtime_client::runtime::Block; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_imports)]` on by default diff --git a/primitives/api/test/tests/ui/mock_only_self_reference.rs b/primitives/api/test/tests/ui/mock_only_self_reference.rs new file mode 100644 index 0000000000000..8a733f5779ce9 --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_self_reference.rs @@ -0,0 +1,20 @@ +use substrate_test_runtime_client::runtime::Block; + +sp_api::decl_runtime_apis! { + pub trait Api { + fn test(data: u64); + fn test2(data: u64); + } +} + +struct MockApi; + +sp_api::mock_impl_runtime_apis! { + impl Api for MockApi { + fn test(self, data: u64) {} + + fn test2(&mut self, data: u64) {} + } +} + +fn main() {} diff --git a/primitives/api/test/tests/ui/mock_only_self_reference.stderr b/primitives/api/test/tests/ui/mock_only_self_reference.stderr new file mode 100644 index 0000000000000..9c1658b0a6cea --- /dev/null +++ b/primitives/api/test/tests/ui/mock_only_self_reference.stderr @@ -0,0 +1,65 @@ +error: Only `&self` is supported! + --> $DIR/mock_only_self_reference.rs:14:11 + | +14 | fn test(self, data: u64) {} + | ^^^^ + +error: Only `&self` is supported! + --> $DIR/mock_only_self_reference.rs:16:12 + | +16 | fn test2(&mut self, data: u64) {} + | ^ + +error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for trait + --> $DIR/mock_only_self_reference.rs:12:1 + | +3 | / sp_api::decl_runtime_apis! { +4 | | pub trait Api { +5 | | fn test(data: u64); +6 | | fn test2(data: u64); +7 | | } +8 | | } + | |_- type in trait +... +12 | sp_api::mock_impl_runtime_apis! { + | -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | _expected `u64`, found `()` + | | +13 | | impl Api for MockApi { +14 | | fn test(self, data: u64) {} +15 | | +16 | | fn test2(&mut self, data: u64) {} +17 | | } +18 | | } + | |_- in this macro invocation + | + = note: expected fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec<_>) -> std::result::Result<_, _>` + found fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option<()>, std::vec::Vec<_>) -> std::result::Result<_, _>` + +error[E0053]: method `Api_test2_runtime_api_impl` has an incompatible type for trait + --> $DIR/mock_only_self_reference.rs:12:1 + | +3 | / sp_api::decl_runtime_apis! { +4 | | pub trait Api { +5 | | fn test(data: u64); +6 | | fn test2(data: u64); +7 | | } +8 | | } + | |_- type in trait +... +12 | sp_api::mock_impl_runtime_apis! { + | -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | _expected `u64`, found `()` + | | +13 | | impl Api for MockApi { +14 | | fn test(self, data: u64) {} +15 | | +16 | | fn test2(&mut self, data: u64) {} +17 | | } +18 | | } + | |_- in this macro invocation + | + = note: expected fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec<_>) -> std::result::Result<_, _>` + found fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option<()>, std::vec::Vec<_>) -> std::result::Result<_, _>` From 9942f0b492ca253c9d74280fc6510c09e7ba89c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 30 Mar 2020 01:57:15 +0200 Subject: [PATCH 2/4] Fix warnings --- client/finality-grandpa/src/tests.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index 96bc00ebe4c07..05ce90d3f1f75 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -30,21 +30,17 @@ use sp_keyring::Ed25519Keyring; use sc_client::LongestChain; use sc_client_api::backend::TransactionFor; use sp_blockchain::Result; -use sp_api::{ApiRef, ApiErrorExt, Core, RuntimeVersion, ApiExt, StorageProof, ProvideRuntimeApi}; +use sp_api::{ApiRef, StorageProof, ProvideRuntimeApi}; use substrate_test_runtime_client::runtime::BlockNumber; use sp_consensus::{ BlockOrigin, ForkChoiceStrategy, ImportedAux, BlockImportParams, ImportResult, BlockImport, import_queue::{BoxJustificationImport, BoxFinalityProofImport}, }; -use std::{ - collections::{HashMap, HashSet}, - result, - pin::Pin, -}; +use std::{collections::{HashMap, HashSet}, pin::Pin}; use parity_scale_codec::Decode; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, HashFor}; use sp_runtime::generic::{BlockId, DigestItem}; -use sp_core::{H256, NativeOrEncoded, ExecutionContext, crypto::Public}; +use sp_core::{H256, crypto::Public}; use sp_finality_grandpa::{GRANDPA_ENGINE_ID, AuthorityList, GrandpaApi}; use sp_state_machine::{InMemoryBackend, prove_read, read_proof_check}; From c2ffdedd5b746ae77aa4c522d7167eb87c7395c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 30 Mar 2020 16:27:29 +0200 Subject: [PATCH 3/4] Update primitives/api/proc-macro/src/utils.rs Co-Authored-By: Nikolay Volf --- primitives/api/proc-macro/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/api/proc-macro/src/utils.rs b/primitives/api/proc-macro/src/utils.rs index 90d7ab061d4d5..1a79cf6c1ef5b 100644 --- a/primitives/api/proc-macro/src/utils.rs +++ b/primitives/api/proc-macro/src/utils.rs @@ -238,7 +238,7 @@ pub fn extract_block_type_from_trait_path(trait_: &Path) -> Result<&TypePath> { }, PathArguments::Parenthesized(_) => { Err(Error::new(generics.arguments.span(), "Unexpected parentheses in path!")) - } + }, } } From 577097b3f2ab2531600555f8dc4fa7b6a45af820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 31 Mar 2020 00:00:41 +0200 Subject: [PATCH 4/4] Review feedback --- .../proc-macro/src/mock_impl_runtime_apis.rs | 26 ++++++++++++++----- .../tests/ui/mock_only_one_block_type.stderr | 6 +++++ .../tests/ui/mock_only_one_self_type.stderr | 6 +++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs index 6348a6305f6db..0767c804a637e 100644 --- a/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs @@ -283,8 +283,8 @@ struct GeneratedRuntimeApiImpls { fn generate_runtime_api_impls(impls: &[ItemImpl]) -> Result { let mut result = Vec::with_capacity(impls.len()); let mut error_type = None; - let mut global_block_type = None; - let mut self_ty = None; + let mut global_block_type: Option = None; + let mut self_ty: Option> = None; for impl_ in impls { let impl_trait_path = extract_impl_trait(&impl_, RequireQualifiedTraitPath::No)?; @@ -300,10 +300,17 @@ fn generate_runtime_api_impls(impls: &[ItemImpl]) -> Result Some(impl_.self_ty.clone()), @@ -314,10 +321,17 @@ fn generate_runtime_api_impls(impls: &[ItemImpl]) -> Result Some(block_type.clone()), diff --git a/primitives/api/test/tests/ui/mock_only_one_block_type.stderr b/primitives/api/test/tests/ui/mock_only_one_block_type.stderr index 6330807ba1c63..1abc8db726a16 100644 --- a/primitives/api/test/tests/ui/mock_only_one_block_type.stderr +++ b/primitives/api/test/tests/ui/mock_only_one_block_type.stderr @@ -4,6 +4,12 @@ error: Block type should be the same between all runtime apis. 22 | impl Api2 for MockApi { | ^^^^^^ +error: First block type found here + --> $DIR/mock_only_one_block_type.rs:18:11 + | +18 | impl Api for MockApi { + | ^^^^^ + warning: unused import: `substrate_test_runtime_client::runtime::Block` --> $DIR/mock_only_one_block_type.rs:1:5 | diff --git a/primitives/api/test/tests/ui/mock_only_one_self_type.stderr b/primitives/api/test/tests/ui/mock_only_one_self_type.stderr index 7ef1b2eb0b220..996d1d44c044c 100644 --- a/primitives/api/test/tests/ui/mock_only_one_self_type.stderr +++ b/primitives/api/test/tests/ui/mock_only_one_self_type.stderr @@ -4,6 +4,12 @@ error: Self type should not change between runtime apis 21 | impl Api2 for MockApi2 { | ^^^^^^^^ +error: First self type found here + --> $DIR/mock_only_one_self_type.rs:17:22 + | +17 | impl Api for MockApi { + | ^^^^^^^ + warning: unused import: `substrate_test_runtime_client::runtime::Block` --> $DIR/mock_only_one_self_type.rs:1:5 |