referrerpolicy=no-referrer-when-downgrade

sp_api_proc_macro/
decl_runtime_apis.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
18use crate::{
19	common::{
20		API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE,
21		RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES,
22	},
23	utils::{
24		extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side,
25		generate_crate_access, generate_runtime_mod_name_for_trait, parse_runtime_api_version,
26		prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type,
27		versioned_trait_name, AllowSelfRefInParameters,
28	},
29};
30
31use proc_macro2::{Span, TokenStream};
32
33use quote::quote;
34
35use std::collections::{BTreeMap, HashMap};
36use syn::{
37	fold::{self, Fold},
38	parse::{Error, Parse, ParseStream, Result},
39	parse_macro_input, parse_quote,
40	spanned::Spanned,
41	token::Comma,
42	visit::{self, Visit},
43	Attribute, FnArg, GenericParam, Generics, Ident, ItemTrait, LitInt, LitStr, TraitBound,
44	TraitItem, TraitItemFn,
45};
46
47/// The structure used for parsing the runtime api declarations.
48struct RuntimeApiDecls {
49	decls: Vec<ItemTrait>,
50}
51
52impl Parse for RuntimeApiDecls {
53	fn parse(input: ParseStream) -> Result<Self> {
54		let mut decls = Vec::new();
55
56		while !input.is_empty() {
57			decls.push(ItemTrait::parse(input)?);
58		}
59
60		Ok(Self { decls })
61	}
62}
63
64/// Extend the given generics with `Block: BlockT` as first generic parameter.
65fn extend_generics_with_block(generics: &mut Generics) {
66	let c = generate_crate_access();
67
68	generics.lt_token = Some(Default::default());
69	generics.params.insert(0, parse_quote!( Block: #c::BlockT ));
70	generics.gt_token = Some(Default::default());
71}
72
73/// Remove all attributes from the vector that are supported by us in the declaration of a runtime
74/// api trait. The returned hashmap contains all found attribute names as keys and the rest of the
75/// attribute body as `TokenStream`.
76fn remove_supported_attributes(attrs: &mut Vec<Attribute>) -> HashMap<&'static str, Attribute> {
77	let mut result = HashMap::new();
78	attrs.retain(|v| match SUPPORTED_ATTRIBUTE_NAMES.iter().find(|a| v.path().is_ident(a)) {
79		Some(attribute) => {
80			result.insert(*attribute, v.clone());
81			false
82		},
83		None => true,
84	});
85
86	result
87}
88
89/// Versioned API traits are used to catch missing methods when implementing a specific version of a
90/// versioned API. They contain all non-versioned methods (aka stable methods) from the main trait
91/// and all versioned methods for the specific version. This means that there is one trait for each
92/// version mentioned in the trait definition. For example:
93/// ```ignore
94/// // The trait version implicitly is 1
95/// decl_runtime_apis!(
96/// 	trait SomeApi {
97/// 		fn method1(); 	// this is a 'stable method'
98///
99/// 		#[api_version(2)]
100/// 		fn method2();
101///
102/// 		#[api_version(2)]
103/// 		fn method3();
104///
105/// 		#[api_version(3)]
106/// 		fn method4();
107/// 	}
108/// );
109/// ```
110/// This trait has got three different versions. The function below will generate the following
111/// code:
112/// ```
113/// trait SomeApiV1 {
114/// 	// in V1 only the stable methods are required. The rest has got default implementations.
115/// 	fn method1();
116/// }
117///
118/// trait SomeApiV2 {
119/// 	// V2 contains all methods from V1 and V2. V3 not required so they are skipped.
120/// 	fn method1();
121/// 	fn method2();
122/// 	fn method3();
123/// }
124///
125/// trait SomeApiV3 {
126/// 	// And V3 contains all methods from the trait.
127/// 	fn method1();
128/// 	fn method2();
129/// 	fn method3();
130/// 	fn method4();
131/// }
132/// ```
133fn generate_versioned_api_traits(
134	api: ItemTrait,
135	methods: BTreeMap<u32, Vec<TraitItemFn>>,
136) -> Vec<ItemTrait> {
137	let mut result = Vec::<ItemTrait>::new();
138	for (version, _) in &methods {
139		let mut versioned_trait = api.clone();
140		versioned_trait.ident = versioned_trait_name(&versioned_trait.ident, *version);
141		versioned_trait.items = Vec::new();
142		// Add the methods from the current version and all previous one. Versions are sorted so
143		// it's safe to stop early.
144		for (_, m) in methods.iter().take_while(|(v, _)| v <= &version) {
145			versioned_trait.items.extend(m.iter().cloned().map(|m| TraitItem::Fn(m)));
146		}
147
148		result.push(versioned_trait);
149	}
150
151	result
152}
153
154/// Try to parse the given `Attribute` as `renamed` attribute.
155fn parse_renamed_attribute(renamed: &Attribute) -> Result<(String, u32)> {
156	let err = || {
157		Error::new(
158			renamed.span(),
159			&format!(
160				"Unexpected `{RENAMED_ATTRIBUTE}` attribute. \
161				 The supported format is `{RENAMED_ATTRIBUTE}(\"old_name\", version_it_was_renamed)`",
162			),
163		)
164	};
165
166	renamed
167		.parse_args_with(|input: ParseStream| {
168			let old_name: LitStr = input.parse()?;
169			let _comma: Comma = input.parse()?;
170			let version: LitInt = input.parse()?;
171
172			if !input.is_empty() {
173				return Err(input.error("No more arguments expected"))
174			}
175
176			Ok((old_name.value(), version.base10_parse()?))
177		})
178		.map_err(|_| err())
179}
180
181/// Generate the declaration of the trait for the runtime.
182fn generate_runtime_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
183	let mut result = Vec::new();
184
185	for decl in decls {
186		let mut decl = decl.clone();
187		let decl_span = decl.span();
188		extend_generics_with_block(&mut decl.generics);
189		let mod_name = generate_runtime_mod_name_for_trait(&decl.ident);
190
191		let found_attributes = remove_supported_attributes(&mut decl.attrs);
192		let api_version = get_api_version(&found_attributes).map(generate_runtime_api_version)?;
193		let id = generate_runtime_api_id(&decl.ident.to_string());
194
195		let trait_api_version = get_api_version(&found_attributes)?;
196
197		let mut methods_by_version: BTreeMap<u32, Vec<TraitItemFn>> = BTreeMap::new();
198
199		// Process the items in the declaration. The filter_map function below does a lot of stuff
200		// because the method attributes are stripped at this point
201		decl.items.iter_mut().for_each(|i| {
202			match i {
203				TraitItem::Fn(ref mut method) => {
204					let method_attrs = remove_supported_attributes(&mut method.attrs);
205					let mut method_version = trait_api_version;
206					// validate the api version for the method (if any) and generate default
207					// implementation for versioned methods
208					if let Some(version_attribute) = method_attrs.get(API_VERSION_ATTRIBUTE) {
209						method_version = match parse_runtime_api_version(version_attribute) {
210							Ok(method_api_ver) if method_api_ver < trait_api_version => {
211								let method_ver = method_api_ver.to_string();
212								let trait_ver = trait_api_version.to_string();
213								let mut err1 = Error::new(
214									version_attribute.span(),
215									format!(
216										"Method version `{}` is older than (or equal to) trait version `{}`.\
217										 Methods can't define versions older than the trait version.",
218										method_ver,
219										trait_ver,
220									),
221								);
222
223								let err2 = match found_attributes.get(&API_VERSION_ATTRIBUTE) {
224									Some(attr) =>
225										Error::new(attr.span(), "Trait version is set here."),
226									None => Error::new(
227										decl_span,
228										"Trait version is not set so it is implicitly equal to 1.",
229									),
230								};
231								err1.combine(err2);
232								result.push(err1.to_compile_error());
233
234								trait_api_version
235							},
236							Ok(method_api_ver) => method_api_ver,
237							Err(e) => {
238								result.push(e.to_compile_error());
239								trait_api_version
240							},
241						};
242					}
243
244					// Any method with the `changed_in` attribute isn't required for the runtime
245					// anymore.
246					if !method_attrs.contains_key(CHANGED_IN_ATTRIBUTE) {
247						// Make sure we replace all the wild card parameter names.
248						replace_wild_card_parameter_names(&mut method.sig);
249
250						// partition methods by api version
251						methods_by_version.entry(method_version).or_default().push(method.clone());
252					}
253				},
254				_ => (),
255			}
256		});
257
258		let versioned_methods_iter = methods_by_version
259			.iter()
260			.flat_map(|(&version, methods)| methods.iter().map(move |method| (method, version)));
261		let metadata =
262			crate::runtime_metadata::generate_decl_runtime_metadata(&decl, versioned_methods_iter);
263
264		let versioned_api_traits = generate_versioned_api_traits(decl.clone(), methods_by_version);
265
266		let main_api_ident = decl.ident.clone();
267		let versioned_ident = &versioned_api_traits
268			.first()
269			.expect("There should always be at least one version.")
270			.ident;
271
272		result.push(quote!(
273			#[doc(hidden)]
274			#[allow(dead_code)]
275			#[allow(deprecated)]
276			pub mod #mod_name {
277				pub use super::*;
278
279				#( #versioned_api_traits )*
280
281				pub use #versioned_ident as #main_api_ident;
282
283				#metadata
284
285				pub #api_version
286
287				pub #id
288			}
289		));
290	}
291
292	Ok(quote!( #( #result )* ))
293}
294
295/// Modify the given runtime api declaration to be usable on the client side.
296struct ToClientSideDecl<'a> {
297	block_hash: &'a TokenStream,
298	crate_: &'a TokenStream,
299	found_attributes: &'a mut HashMap<&'static str, Attribute>,
300	/// Any error that we found while converting this declaration.
301	errors: &'a mut Vec<TokenStream>,
302	trait_: &'a Ident,
303}
304
305impl<'a> ToClientSideDecl<'a> {
306	/// Process the given [`ItemTrait`].
307	fn process(mut self, decl: ItemTrait) -> ItemTrait {
308		let mut decl = self.fold_item_trait(decl);
309
310		let block_hash = self.block_hash;
311		let crate_ = self.crate_;
312
313		// Add the special method that will be implemented by the `impl_runtime_apis!` macro
314		// to enable functions to call into the runtime.
315		decl.items.push(parse_quote! {
316			/// !!INTERNAL USE ONLY!!
317			#[doc(hidden)]
318			fn __runtime_api_internal_call_api_at(
319				&self,
320				at: #block_hash,
321				params: std::vec::Vec<u8>,
322				fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
323			) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError>;
324		});
325
326		decl
327	}
328}
329
330impl<'a> ToClientSideDecl<'a> {
331	fn fold_item_trait_items(
332		&mut self,
333		items: Vec<TraitItem>,
334		trait_generics_num: usize,
335	) -> Vec<TraitItem> {
336		let mut result = Vec::new();
337
338		items.into_iter().for_each(|i| match i {
339			TraitItem::Fn(method) => {
340				let fn_decl = self.create_method_decl(method, trait_generics_num);
341				result.push(fn_decl.into());
342			},
343			r => result.push(r),
344		});
345
346		result
347	}
348
349	/// Takes the method declared by the user and creates the declaration we require for the runtime
350	/// api client side. This method will call by default the `method_runtime_api_impl` for doing
351	/// the actual call into the runtime.
352	fn create_method_decl(
353		&mut self,
354		mut method: TraitItemFn,
355		trait_generics_num: usize,
356	) -> TraitItemFn {
357		let params = match extract_parameter_names_types_and_borrows(
358			&method.sig,
359			AllowSelfRefInParameters::No,
360		) {
361			Ok(res) => res.into_iter().map(|v| v.0).collect::<Vec<_>>(),
362			Err(e) => {
363				self.errors.push(e.to_compile_error());
364				Vec::new()
365			},
366		};
367		let ret_type = return_type_extract_type(&method.sig.output);
368
369		fold_fn_decl_for_client_side(&mut method.sig, self.block_hash, self.crate_);
370
371		let crate_ = self.crate_;
372
373		let found_attributes = remove_supported_attributes(&mut method.attrs);
374
375		// Parse the renamed attributes.
376		let mut renames = Vec::new();
377		for (_, a) in found_attributes.iter().filter(|a| a.0 == &RENAMED_ATTRIBUTE) {
378			match parse_renamed_attribute(a) {
379				Ok((old_name, version)) => {
380					renames.push((version, prefix_function_with_trait(&self.trait_, &old_name)));
381				},
382				Err(e) => self.errors.push(e.to_compile_error()),
383			}
384		}
385
386		renames.sort_by(|l, r| r.cmp(l));
387		let (versions, old_names) = renames.into_iter().fold(
388			(Vec::new(), Vec::new()),
389			|(mut versions, mut old_names), (version, old_name)| {
390				versions.push(version);
391				old_names.push(old_name);
392				(versions, old_names)
393			},
394		);
395
396		// Generate the function name before we may rename it below to
397		// `function_name_before_version_{}`.
398		let function_name = prefix_function_with_trait(&self.trait_, &method.sig.ident);
399
400		// If the method has a `changed_in` attribute, we need to alter the method name to
401		// `method_before_version_VERSION`.
402		match get_changed_in(&found_attributes) {
403			Ok(Some(version)) => {
404				// Make sure that the `changed_in` version is at least the current `api_version`.
405				if get_api_version(self.found_attributes).ok() < Some(version) {
406					self.errors.push(
407						Error::new(
408							method.span(),
409							"`changed_in` version can not be greater than the `api_version`",
410						)
411						.to_compile_error(),
412					);
413				}
414
415				let ident = Ident::new(
416					&format!("{}_before_version_{}", method.sig.ident, version),
417					method.sig.ident.span(),
418				);
419				method.sig.ident = ident;
420				method.attrs.push(parse_quote!( #[deprecated] ));
421			},
422			Ok(None) => {},
423			Err(e) => {
424				self.errors.push(e.to_compile_error());
425			},
426		};
427
428		// The module where the runtime relevant stuff is declared.
429		let trait_name = &self.trait_;
430		let runtime_mod = generate_runtime_mod_name_for_trait(trait_name);
431		let underscores = (0..trait_generics_num).map(|_| quote!(_));
432
433		// Generate the default implementation that calls the `method_runtime_api_impl` method.
434		method.default = Some(parse_quote! {
435			{
436				let __runtime_api_impl_params_encoded__ =
437					#crate_::Encode::encode(&( #( &#params ),* ));
438
439				<Self as #trait_name<#( #underscores ),*>>::__runtime_api_internal_call_api_at(
440					self,
441					__runtime_api_at_param__,
442					__runtime_api_impl_params_encoded__,
443					&|_version| {
444						#(
445							// Check if we need to call the function by an old name.
446							if _version.apis.iter().any(|(s, v)| {
447								s == &#runtime_mod::ID && *v < #versions
448							}) {
449								return #old_names
450							}
451						)*
452
453						#function_name
454					}
455				)
456				.and_then(|r|
457					std::result::Result::map_err(
458						<#ret_type as #crate_::Decode>::decode(&mut &r[..]),
459						|err| #crate_::ApiError::FailedToDecodeReturnValue {
460							function: #function_name,
461							error: err,
462							raw: r.clone(),
463						}
464					)
465				)
466			}
467		});
468
469		method
470	}
471}
472
473impl<'a> Fold for ToClientSideDecl<'a> {
474	fn fold_item_trait(&mut self, mut input: ItemTrait) -> ItemTrait {
475		extend_generics_with_block(&mut input.generics);
476
477		*self.found_attributes = remove_supported_attributes(&mut input.attrs);
478		// Check if this is the `Core` runtime api trait.
479		let is_core_trait = self.found_attributes.contains_key(CORE_TRAIT_ATTRIBUTE);
480		let block_ident = Ident::new(BLOCK_GENERIC_IDENT, Span::call_site());
481
482		if is_core_trait {
483			// Add all the supertraits we want to have for `Core`.
484			input.supertraits = parse_quote!('static + Send);
485		} else {
486			// Add the `Core` runtime api as super trait.
487			let crate_ = &self.crate_;
488			input.supertraits.push(parse_quote!( #crate_::Core<#block_ident> ));
489		}
490
491		input.items = self.fold_item_trait_items(input.items, input.generics.params.len());
492
493		fold::fold_item_trait(self, input)
494	}
495}
496
497/// Generates the identifier as const variable for the given `trait_name`
498/// by hashing the `trait_name`.
499fn generate_runtime_api_id(trait_name: &str) -> TokenStream {
500	use blake2::digest::{consts::U8, Digest};
501
502	let mut res = [0; 8];
503	res.copy_from_slice(blake2::Blake2b::<U8>::digest(trait_name).as_slice());
504
505	quote!( const ID: [u8; 8] = [ #( #res ),* ]; )
506}
507
508/// Generates the const variable that holds the runtime api version.
509fn generate_runtime_api_version(version: u32) -> TokenStream {
510	quote!( const VERSION: u32 = #version; )
511}
512
513/// Generates the implementation of `RuntimeApiInfo` for the given trait.
514fn generate_runtime_info_impl(trait_: &ItemTrait, version: u32) -> TokenStream {
515	let trait_name = &trait_.ident;
516	let crate_ = generate_crate_access();
517	let id = generate_runtime_api_id(&trait_name.to_string());
518	let version = generate_runtime_api_version(version as u32);
519
520	let impl_generics = trait_.generics.type_params().map(|t| {
521		let ident = &t.ident;
522		let colon_token = &t.colon_token;
523		let bounds = &t.bounds;
524
525		quote! { #ident #colon_token #bounds }
526	});
527
528	let ty_generics = trait_.generics.type_params().map(|t| {
529		let ident = &t.ident;
530		quote! { #ident }
531	});
532	let maybe_allow_attrs = trait_.attrs.iter().filter(|attr| attr.path().is_ident("allow"));
533	let maybe_allow_deprecated = trait_.attrs.iter().filter_map(|attr| {
534		attr.path().is_ident("deprecated").then(|| quote! { #[allow(deprecated)] })
535	});
536
537	quote!(
538		#crate_::std_enabled! {
539			#( #maybe_allow_attrs )*
540			#( #maybe_allow_deprecated )*
541			impl < #( #impl_generics, )* > #crate_::RuntimeApiInfo
542				for dyn #trait_name < #( #ty_generics, )* >
543			{
544				#id
545				#version
546			}
547		}
548	)
549}
550
551/// Get changed in version from the user given attribute or `Ok(None)`, if no attribute was given.
552fn get_changed_in(found_attributes: &HashMap<&'static str, Attribute>) -> Result<Option<u32>> {
553	found_attributes
554		.get(&CHANGED_IN_ATTRIBUTE)
555		.map(|v| parse_runtime_api_version(v).map(Some))
556		.unwrap_or(Ok(None))
557}
558
559/// Get the api version from the user given attribute or `Ok(1)`, if no attribute was given.
560fn get_api_version(found_attributes: &HashMap<&'static str, Attribute>) -> Result<u32> {
561	found_attributes
562		.get(&API_VERSION_ATTRIBUTE)
563		.map(parse_runtime_api_version)
564		.unwrap_or(Ok(1))
565}
566
567/// Generate the declaration of the trait for the client side.
568fn generate_client_side_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
569	let mut result = Vec::new();
570
571	for decl in decls {
572		let decl = decl.clone();
573
574		let crate_ = generate_crate_access();
575		let block_hash = quote!( <Block as #crate_::BlockT>::Hash );
576		let mut found_attributes = HashMap::new();
577		let mut errors = Vec::new();
578		let trait_ = decl.ident.clone();
579
580		let decl = ToClientSideDecl {
581			crate_: &crate_,
582			block_hash: &block_hash,
583			found_attributes: &mut found_attributes,
584			errors: &mut errors,
585			trait_: &trait_,
586		}
587		.process(decl);
588
589		let api_version = get_api_version(&found_attributes);
590
591		let runtime_info = api_version.map(|v| generate_runtime_info_impl(&decl, v))?;
592
593		result.push(quote!(
594			#crate_::std_enabled! {
595				#[allow(deprecated)]
596				#decl
597			 }
598			#runtime_info
599			#( #errors )*
600		));
601	}
602
603	Ok(quote!( #( #result )* ))
604}
605
606/// Checks that a trait declaration is in the format we expect.
607struct CheckTraitDecl {
608	errors: Vec<Error>,
609}
610
611impl CheckTraitDecl {
612	/// Check the given trait.
613	///
614	/// All errors will be collected in `self.errors`.
615	fn check(&mut self, trait_: &ItemTrait) {
616		self.check_method_declarations(trait_.items.iter().filter_map(|i| match i {
617			TraitItem::Fn(method) => Some(method),
618			_ => None,
619		}));
620
621		visit::visit_item_trait(self, trait_);
622	}
623
624	/// Check that the given method declarations are correct.
625	///
626	/// Any error is stored in `self.errors`.
627	fn check_method_declarations<'a>(&mut self, methods: impl Iterator<Item = &'a TraitItemFn>) {
628		let mut method_to_signature_changed = HashMap::<Ident, Vec<Option<u32>>>::new();
629
630		methods.into_iter().for_each(|method| {
631			let attributes = remove_supported_attributes(&mut method.attrs.clone());
632
633			let changed_in = match get_changed_in(&attributes) {
634				Ok(r) => r,
635				Err(e) => {
636					self.errors.push(e);
637					return
638				},
639			};
640
641			method_to_signature_changed
642				.entry(method.sig.ident.clone())
643				.or_default()
644				.push(changed_in);
645
646			if method.default.is_some() {
647				self.errors.push(Error::new(
648					method.default.span(),
649					"A runtime API function cannot have a default implementation!",
650				));
651			}
652		});
653
654		method_to_signature_changed.into_iter().for_each(|(f, changed)| {
655			// If `changed_in` is `None`, it means it is the current "default" method that calls
656			// into the latest implementation.
657			if changed.iter().filter(|c| c.is_none()).count() == 0 {
658				self.errors.push(Error::new(
659					f.span(),
660					"There is no 'default' method with this name (without `changed_in` attribute).\n\
661					 The 'default' method is used to call into the latest implementation.",
662				));
663			}
664		});
665	}
666}
667
668impl<'ast> Visit<'ast> for CheckTraitDecl {
669	fn visit_fn_arg(&mut self, input: &'ast FnArg) {
670		if let FnArg::Receiver(_) = input {
671			self.errors.push(Error::new(input.span(), "`self` as argument not supported."))
672		}
673
674		visit::visit_fn_arg(self, input);
675	}
676
677	fn visit_generic_param(&mut self, input: &'ast GenericParam) {
678		match input {
679			GenericParam::Type(ty) if ty.ident == BLOCK_GENERIC_IDENT =>
680				self.errors.push(Error::new(
681					input.span(),
682					"`Block: BlockT` generic parameter will be added automatically by the \
683						`decl_runtime_apis!` macro!",
684				)),
685			_ => {},
686		}
687
688		visit::visit_generic_param(self, input);
689	}
690
691	fn visit_trait_bound(&mut self, input: &'ast TraitBound) {
692		if let Some(last_ident) = input.path.segments.last().map(|v| &v.ident) {
693			if last_ident == "BlockT" || last_ident == BLOCK_GENERIC_IDENT {
694				self.errors.push(Error::new(
695					input.span(),
696					"`Block: BlockT` generic parameter will be added automatically by the \
697						`decl_runtime_apis!` macro! If you try to use a different trait than the \
698						substrate `Block` trait, please rename it locally.",
699				))
700			}
701		}
702
703		visit::visit_trait_bound(self, input)
704	}
705}
706
707/// Check that the trait declarations are in the format we expect.
708fn check_trait_decls(decls: &[ItemTrait]) -> Result<()> {
709	let mut checker = CheckTraitDecl { errors: Vec::new() };
710	decls.iter().for_each(|decl| checker.check(decl));
711
712	if let Some(err) = checker.errors.pop() {
713		Err(checker.errors.into_iter().fold(err, |mut err, other| {
714			err.combine(other);
715			err
716		}))
717	} else {
718		Ok(())
719	}
720}
721
722/// The implementation of the `decl_runtime_apis!` macro.
723pub fn decl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
724	// Parse all trait declarations
725	let RuntimeApiDecls { decls: api_decls } = parse_macro_input!(input as RuntimeApiDecls);
726
727	decl_runtime_apis_impl_inner(&api_decls)
728		.unwrap_or_else(|e| e.to_compile_error())
729		.into()
730}
731
732fn decl_runtime_apis_impl_inner(api_decls: &[ItemTrait]) -> Result<TokenStream> {
733	check_trait_decls(api_decls)?;
734
735	let runtime_decls = generate_runtime_decls(api_decls)?;
736	let client_side_decls = generate_client_side_decls(api_decls)?;
737
738	let decl = quote! {
739		#runtime_decls
740
741		#client_side_decls
742	};
743
744	let decl = expander::Expander::new("decl_runtime_apis")
745		.dry(std::env::var("EXPAND_MACROS").is_err())
746		.verbose(true)
747		.write_to_out_dir(decl)
748		.expect("Does not fail because of IO in OUT_DIR; qed");
749
750	Ok(decl)
751}