mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-08 15:58:02 +00:00
002d9260f9
**Update:** Pushed additional changes based on the review comments. **This pull request fixes various spelling mistakes in this repository.** Most of the changes are contained in the first **3** commits: - `Fix spelling mistakes in comments and docs` - `Fix spelling mistakes in test names` - `Fix spelling mistakes in error messages, panic messages, logs and tracing` Other source code spelling mistakes are separated into individual commits for easier reviewing: - `Fix the spelling of 'authority'` - `Fix the spelling of 'REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY'` - `Fix the spelling of 'prev_enqueud_messages'` - `Fix the spelling of 'endpoint'` - `Fix the spelling of 'children'` - `Fix the spelling of 'PenpalSiblingSovereignAccount'` - `Fix the spelling of 'PenpalSudoAccount'` - `Fix the spelling of 'insufficient'` - `Fix the spelling of 'PalletXcmExtrinsicsBenchmark'` - `Fix the spelling of 'subtracted'` - `Fix the spelling of 'CandidatePendingAvailability'` - `Fix the spelling of 'exclusive'` - `Fix the spelling of 'until'` - `Fix the spelling of 'discriminator'` - `Fix the spelling of 'nonexistent'` - `Fix the spelling of 'subsystem'` - `Fix the spelling of 'indices'` - `Fix the spelling of 'committed'` - `Fix the spelling of 'topology'` - `Fix the spelling of 'response'` - `Fix the spelling of 'beneficiary'` - `Fix the spelling of 'formatted'` - `Fix the spelling of 'UNKNOWN_PROOF_REQUEST'` - `Fix the spelling of 'succeeded'` - `Fix the spelling of 'reopened'` - `Fix the spelling of 'proposer'` - `Fix the spelling of 'InstantiationNonce'` - `Fix the spelling of 'depositor'` - `Fix the spelling of 'expiration'` - `Fix the spelling of 'phantom'` - `Fix the spelling of 'AggregatedKeyValue'` - `Fix the spelling of 'randomness'` - `Fix the spelling of 'defendant'` - `Fix the spelling of 'AquaticMammal'` - `Fix the spelling of 'transactions'` - `Fix the spelling of 'PassingTracingSubscriber'` - `Fix the spelling of 'TxSignaturePayload'` - `Fix the spelling of 'versioning'` - `Fix the spelling of 'descendant'` - `Fix the spelling of 'overridden'` - `Fix the spelling of 'network'` Let me know if this structure is adequate. **Note:** The usage of the words `Merkle`, `Merkelize`, `Merklization`, `Merkelization`, `Merkleization`, is somewhat inconsistent but I left it as it is. ~~**Note:** In some places the term `Receival` is used to refer to message reception, IMO `Reception` is the correct word here, but I left it as it is.~~ ~~**Note:** In some places the term `Overlayed` is used instead of the more acceptable version `Overlaid` but I also left it as it is.~~ ~~**Note:** In some places the term `Applyable` is used instead of the correct version `Applicable` but I also left it as it is.~~ **Note:** Some usage of British vs American english e.g. `judgement` vs `judgment`, `initialise` vs `initialize`, `optimise` vs `optimize` etc. are both present in different places, but I suppose that's understandable given the number of contributors. ~~**Note:** There is a spelling mistake in `.github/CODEOWNERS` but it triggers errors in CI when I make changes to it, so I left it as it is.~~
304 lines
9.8 KiB
Rust
304 lines
9.8 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Implementation of the `derive_impl` attribute macro.
|
|
|
|
use derive_syn_parse::Parse;
|
|
use macro_magic::mm_core::ForeignPath;
|
|
use proc_macro2::TokenStream as TokenStream2;
|
|
use quote::{quote, ToTokens};
|
|
use std::collections::HashSet;
|
|
use syn::{
|
|
parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token,
|
|
};
|
|
|
|
mod keyword {
|
|
syn::custom_keyword!(inject_runtime_type);
|
|
syn::custom_keyword!(no_aggregated_types);
|
|
}
|
|
|
|
#[derive(derive_syn_parse::Parse, PartialEq, Eq)]
|
|
pub enum PalletAttrType {
|
|
#[peek(keyword::inject_runtime_type, name = "inject_runtime_type")]
|
|
RuntimeType(keyword::inject_runtime_type),
|
|
}
|
|
|
|
#[derive(derive_syn_parse::Parse)]
|
|
pub struct PalletAttr {
|
|
_pound: Token![#],
|
|
#[bracket]
|
|
_bracket: token::Bracket,
|
|
#[inside(_bracket)]
|
|
typ: PalletAttrType,
|
|
}
|
|
|
|
fn is_runtime_type(item: &syn::ImplItemType) -> bool {
|
|
item.attrs.iter().any(|attr| {
|
|
if let Ok(PalletAttr { typ: PalletAttrType::RuntimeType(_), .. }) =
|
|
parse2::<PalletAttr>(attr.into_token_stream())
|
|
{
|
|
return true
|
|
}
|
|
false
|
|
})
|
|
}
|
|
|
|
#[derive(Parse, Debug)]
|
|
pub struct DeriveImplAttrArgs {
|
|
pub default_impl_path: Path,
|
|
_as: Option<Token![as]>,
|
|
#[parse_if(_as.is_some())]
|
|
pub disambiguation_path: Option<Path>,
|
|
_comma: Option<Token![,]>,
|
|
#[parse_if(_comma.is_some())]
|
|
pub no_aggregated_types: Option<keyword::no_aggregated_types>,
|
|
}
|
|
|
|
impl ForeignPath for DeriveImplAttrArgs {
|
|
fn foreign_path(&self) -> &Path {
|
|
&self.default_impl_path
|
|
}
|
|
}
|
|
|
|
impl ToTokens for DeriveImplAttrArgs {
|
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
tokens.extend(self.default_impl_path.to_token_stream());
|
|
tokens.extend(self._as.to_token_stream());
|
|
tokens.extend(self.disambiguation_path.to_token_stream());
|
|
tokens.extend(self._comma.to_token_stream());
|
|
tokens.extend(self.no_aggregated_types.to_token_stream());
|
|
}
|
|
}
|
|
|
|
/// Gets the [`Ident`] representation of the given [`ImplItem`], if one exists. Otherwise
|
|
/// returns [`None`].
|
|
///
|
|
/// Used by [`combine_impls`] to determine whether we can compare [`ImplItem`]s by [`Ident`]
|
|
/// or not.
|
|
fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> {
|
|
match impl_item {
|
|
ImplItem::Const(item) => Some(&item.ident),
|
|
ImplItem::Fn(item) => Some(&item.sig.ident),
|
|
ImplItem::Type(item) => Some(&item.ident),
|
|
ImplItem::Macro(item) => item.mac.path.get_ident(),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// The real meat behind `derive_impl`. Takes in a `local_impl`, which is the impl for which we
|
|
/// want to implement defaults (i.e. the one the attribute macro is attached to), and a
|
|
/// `foreign_impl`, which is the impl containing the defaults we want to use, and returns an
|
|
/// [`ItemImpl`] containing the final generated impl.
|
|
///
|
|
/// This process has the following caveats:
|
|
/// * Colliding items that have an ident are not copied into `local_impl`
|
|
/// * Uncolliding items that have an ident are copied into `local_impl` but are qualified as `type
|
|
/// #ident = <#default_impl_path as #disambiguation_path>::#ident;`
|
|
/// * Items that lack an ident are de-duplicated so only unique items that lack an ident are copied
|
|
/// into `local_impl`. Items that lack an ident and also exist verbatim in `local_impl` are not
|
|
/// copied over.
|
|
fn combine_impls(
|
|
local_impl: ItemImpl,
|
|
foreign_impl: ItemImpl,
|
|
default_impl_path: Path,
|
|
disambiguation_path: Path,
|
|
inject_runtime_types: bool,
|
|
) -> ItemImpl {
|
|
let (existing_local_keys, existing_unsupported_items): (HashSet<ImplItem>, HashSet<ImplItem>) =
|
|
local_impl
|
|
.items
|
|
.iter()
|
|
.cloned()
|
|
.partition(|impl_item| impl_item_ident(impl_item).is_some());
|
|
let existing_local_keys: HashSet<Ident> = existing_local_keys
|
|
.into_iter()
|
|
.filter_map(|item| impl_item_ident(&item).cloned())
|
|
.collect();
|
|
let mut final_impl = local_impl;
|
|
let extended_items = foreign_impl.items.into_iter().filter_map(|item| {
|
|
if let Some(ident) = impl_item_ident(&item) {
|
|
if existing_local_keys.contains(&ident) {
|
|
// do not copy colliding items that have an ident
|
|
return None
|
|
}
|
|
if let ImplItem::Type(typ) = item.clone() {
|
|
let cfg_attrs = typ
|
|
.attrs
|
|
.iter()
|
|
.filter(|attr| attr.path().get_ident().map_or(false, |ident| ident == "cfg"))
|
|
.map(|attr| attr.to_token_stream());
|
|
if is_runtime_type(&typ) {
|
|
let item: ImplItem = if inject_runtime_types {
|
|
parse_quote! {
|
|
#( #cfg_attrs )*
|
|
type #ident = #ident;
|
|
}
|
|
} else {
|
|
item
|
|
};
|
|
return Some(item)
|
|
}
|
|
// modify and insert uncolliding type items
|
|
let modified_item: ImplItem = parse_quote! {
|
|
#( #cfg_attrs )*
|
|
type #ident = <#default_impl_path as #disambiguation_path>::#ident;
|
|
};
|
|
return Some(modified_item)
|
|
}
|
|
// copy uncolliding non-type items that have an ident
|
|
Some(item)
|
|
} else {
|
|
// do not copy colliding items that lack an ident
|
|
(!existing_unsupported_items.contains(&item))
|
|
// copy uncolliding items without an ident verbatim
|
|
.then_some(item)
|
|
}
|
|
});
|
|
final_impl.items.extend(extended_items);
|
|
final_impl
|
|
}
|
|
|
|
/// Computes the disambiguation path for the `derive_impl` attribute macro.
|
|
///
|
|
/// When specified explicitly using `as [disambiguation_path]` in the macro attr, the
|
|
/// disambiguation is used as is. If not, we infer the disambiguation path from the
|
|
/// `foreign_impl_path` and the computed scope.
|
|
fn compute_disambiguation_path(
|
|
disambiguation_path: Option<Path>,
|
|
foreign_impl: ItemImpl,
|
|
default_impl_path: Path,
|
|
) -> Result<Path> {
|
|
match (disambiguation_path, foreign_impl.clone().trait_) {
|
|
(Some(disambiguation_path), _) => Ok(disambiguation_path),
|
|
(None, Some((_, foreign_impl_path, _))) =>
|
|
if default_impl_path.segments.len() > 1 {
|
|
let scope = default_impl_path.segments.first();
|
|
Ok(parse_quote!(#scope :: #foreign_impl_path))
|
|
} else {
|
|
Ok(foreign_impl_path)
|
|
},
|
|
_ => Err(syn::Error::new(
|
|
default_impl_path.span(),
|
|
"Impl statement must have a defined type being implemented \
|
|
for a defined type such as `impl A for B`",
|
|
)),
|
|
}
|
|
}
|
|
|
|
/// Internal implementation behind [`#[derive_impl(..)]`](`macro@crate::derive_impl`).
|
|
///
|
|
/// `default_impl_path`: the module path of the external `impl` statement whose tokens we are
|
|
/// importing via `macro_magic`
|
|
///
|
|
/// `foreign_tokens`: the tokens for the external `impl` statement
|
|
///
|
|
/// `local_tokens`: the tokens for the local `impl` statement this attribute is attached to
|
|
///
|
|
/// `disambiguation_path`: the module path of the external trait we will use to qualify
|
|
/// defaults imported from the external `impl` statement
|
|
pub fn derive_impl(
|
|
default_impl_path: TokenStream2,
|
|
foreign_tokens: TokenStream2,
|
|
local_tokens: TokenStream2,
|
|
disambiguation_path: Option<Path>,
|
|
no_aggregated_types: Option<keyword::no_aggregated_types>,
|
|
) -> Result<TokenStream2> {
|
|
let local_impl = parse2::<ItemImpl>(local_tokens)?;
|
|
let foreign_impl = parse2::<ItemImpl>(foreign_tokens)?;
|
|
let default_impl_path = parse2::<Path>(default_impl_path)?;
|
|
|
|
let disambiguation_path = compute_disambiguation_path(
|
|
disambiguation_path,
|
|
foreign_impl.clone(),
|
|
default_impl_path.clone(),
|
|
)?;
|
|
|
|
// generate the combined impl
|
|
let combined_impl = combine_impls(
|
|
local_impl,
|
|
foreign_impl,
|
|
default_impl_path,
|
|
disambiguation_path,
|
|
no_aggregated_types.is_none(),
|
|
);
|
|
|
|
Ok(quote!(#combined_impl))
|
|
}
|
|
|
|
#[test]
|
|
fn test_derive_impl_attr_args_parsing() {
|
|
parse2::<DeriveImplAttrArgs>(quote!(
|
|
some::path::TestDefaultConfig as some::path::DefaultConfig
|
|
))
|
|
.unwrap();
|
|
parse2::<DeriveImplAttrArgs>(quote!(
|
|
frame_system::prelude::testing::TestDefaultConfig as DefaultConfig
|
|
))
|
|
.unwrap();
|
|
parse2::<DeriveImplAttrArgs>(quote!(Something as some::path::DefaultConfig)).unwrap();
|
|
parse2::<DeriveImplAttrArgs>(quote!(Something as DefaultConfig)).unwrap();
|
|
parse2::<DeriveImplAttrArgs>(quote!(DefaultConfig)).unwrap();
|
|
assert!(parse2::<DeriveImplAttrArgs>(quote!()).is_err());
|
|
assert!(parse2::<DeriveImplAttrArgs>(quote!(Config Config)).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_runtime_type_with_doc() {
|
|
trait TestTrait {
|
|
type Test;
|
|
}
|
|
#[allow(unused)]
|
|
struct TestStruct;
|
|
let p = parse2::<ItemImpl>(quote!(
|
|
impl TestTrait for TestStruct {
|
|
/// Some doc
|
|
#[inject_runtime_type]
|
|
type Test = u32;
|
|
}
|
|
))
|
|
.unwrap();
|
|
for item in p.items {
|
|
if let ImplItem::Type(typ) = item {
|
|
assert_eq!(is_runtime_type(&typ), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_disambiguation_path() {
|
|
let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {});
|
|
let default_impl_path: Path = parse_quote!(SomeScope::SomeType);
|
|
|
|
// disambiguation path is specified
|
|
let disambiguation_path = compute_disambiguation_path(
|
|
Some(parse_quote!(SomeScope::SomePath)),
|
|
foreign_impl.clone(),
|
|
default_impl_path.clone(),
|
|
);
|
|
assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomePath));
|
|
|
|
// disambiguation path is not specified and the default_impl_path has more than one segment
|
|
let disambiguation_path =
|
|
compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone());
|
|
assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomeTrait));
|
|
|
|
// disambiguation path is not specified and the default_impl_path has only one segment
|
|
let disambiguation_path =
|
|
compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType));
|
|
assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait));
|
|
}
|