sp_runtime_interface_proc_macro/runtime_interface/
trait_decl_impl.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Checks the trait declaration, makes the trait declaration module local, removes all method
19//! default implementations and implements the trait for `&mut dyn Externalities`.
20
21use crate::utils::{
22	create_function_ident_with_version, generate_crate_access,
23	get_function_argument_types_without_ref, get_runtime_interface,
24};
25
26use syn::{
27	fold::{self, Fold},
28	spanned::Spanned,
29	Error, Generics, ItemTrait, Receiver, Result, TraitItemFn, Type, Visibility,
30};
31
32use proc_macro2::TokenStream;
33
34use quote::quote;
35
36/// Process the given trait definition, by checking that the definition is valid, fold it to the
37/// essential definition and implement this essential definition for `dyn Externalities`.
38pub fn process(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
39	let impl_trait = impl_trait_for_externalities(trait_def, is_wasm_only)?;
40	let essential_trait_def = declare_essential_trait(trait_def)?;
41
42	Ok(quote! {
43		#impl_trait
44
45		#essential_trait_def
46	})
47}
48
49/// Converts the given trait definition into the essential trait definition without method
50/// default implementations and visibility set to inherited.
51struct ToEssentialTraitDef {
52	/// All errors found while doing the conversion.
53	errors: Vec<Error>,
54	methods: Vec<TraitItemFn>,
55}
56
57impl ToEssentialTraitDef {
58	fn new() -> Self {
59		ToEssentialTraitDef { errors: vec![], methods: vec![] }
60	}
61
62	fn into_methods(self) -> Result<Vec<TraitItemFn>> {
63		let mut errors = self.errors;
64		let methods = self.methods;
65		if let Some(first_error) = errors.pop() {
66			Err(errors.into_iter().fold(first_error, |mut o, n| {
67				o.combine(n);
68				o
69			}))
70		} else {
71			Ok(methods)
72		}
73	}
74
75	fn process(&mut self, method: &TraitItemFn, version: u32) {
76		let mut folded = self.fold_trait_item_fn(method.clone());
77		folded.sig.ident = create_function_ident_with_version(&folded.sig.ident, version);
78		self.methods.push(folded);
79	}
80
81	fn push_error<S: Spanned>(&mut self, span: &S, msg: &str) {
82		self.errors.push(Error::new(span.span(), msg));
83	}
84
85	fn error_on_generic_parameters(&mut self, generics: &Generics) {
86		if let Some(param) = generics.params.first() {
87			self.push_error(param, "Generic parameters not supported.");
88		}
89	}
90}
91
92impl Fold for ToEssentialTraitDef {
93	fn fold_trait_item_fn(&mut self, mut method: TraitItemFn) -> TraitItemFn {
94		if method.default.take().is_none() {
95			self.push_error(&method, "Methods need to have an implementation.");
96		}
97
98		let arg_types = get_function_argument_types_without_ref(&method.sig);
99		arg_types
100			.filter_map(|ty| match *ty {
101				Type::ImplTrait(impl_trait) => Some(impl_trait),
102				_ => None,
103			})
104			.for_each(|invalid| self.push_error(&invalid, "`impl Trait` syntax not supported."));
105
106		self.error_on_generic_parameters(&method.sig.generics);
107
108		method.attrs.retain(|a| !a.path().is_ident("version"));
109
110		fold::fold_trait_item_fn(self, method)
111	}
112
113	fn fold_item_trait(&mut self, mut trait_def: ItemTrait) -> ItemTrait {
114		self.error_on_generic_parameters(&trait_def.generics);
115
116		trait_def.vis = Visibility::Inherited;
117		fold::fold_item_trait(self, trait_def)
118	}
119
120	fn fold_receiver(&mut self, receiver: Receiver) -> Receiver {
121		if receiver.reference.is_none() {
122			self.push_error(&receiver, "Taking `Self` by value is not allowed.");
123		}
124
125		fold::fold_receiver(self, receiver)
126	}
127}
128
129fn declare_essential_trait(trait_def: &ItemTrait) -> Result<TokenStream> {
130	let trait_ = &trait_def.ident;
131
132	if let Some(param) = trait_def.generics.params.first() {
133		return Err(Error::new(param.span(), "Generic parameters not supported."))
134	}
135
136	let interface = get_runtime_interface(trait_def)?;
137	let mut folder = ToEssentialTraitDef::new();
138	for (version, interface_method) in interface.all_versions() {
139		folder.process(interface_method, version);
140	}
141	let methods = folder.into_methods()?;
142
143	Ok(quote! {
144		trait #trait_ {
145			#( #methods )*
146		}
147	})
148}
149
150/// Implements the given trait definition for `dyn Externalities`.
151fn impl_trait_for_externalities(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
152	let trait_ = &trait_def.ident;
153	let crate_ = generate_crate_access();
154	let interface = get_runtime_interface(trait_def)?;
155	let methods = interface.all_versions().map(|(version, method)| {
156		let mut cloned = (*method).clone();
157		cloned.attrs.retain(|a| !a.path().is_ident("version"));
158		cloned.sig.ident = create_function_ident_with_version(&cloned.sig.ident, version);
159		cloned
160	});
161
162	let impl_type = if is_wasm_only {
163		quote!( &mut dyn #crate_::sp_wasm_interface::FunctionContext )
164	} else {
165		quote!( &mut dyn #crate_::Externalities )
166	};
167
168	Ok(quote! {
169		#[cfg(feature = "std")]
170		impl #trait_ for #impl_type {
171			#( #methods )*
172		}
173	})
174}