Allow usage of path in construct_runtime! (#8801)

* Allow usage of path in construct_runtime!

* Fix whitespace

* Fix whitespace

* Make expand_runtime_metadata accept slice instead of Iterator

* Include Call and Event in construct_runtime for testing

* Migrate impl_outer_event to proc macro

* Fix integrity_test_works

* Update UI test expectations

* Factor in module path while generating enum variant or fn names

* Use ParseStream::lookahead for more helpful error messages

* Remove generating outer_event_metadata

* Ensure pallets with different paths but same last path segment can coexist

* Remove unnecessary generated function

* Migrate decl_outer_config to proc macro

* Add default_filter test for expand_outer_origin

* Allow crate, self and super keywords to appear in pallet path

* Add UI test for specifying empty pallet paths in construct_runtime
This commit is contained in:
Keith Yeung
2021-06-01 02:23:41 -07:00
committed by GitHub
parent f85492dcbf
commit 3b1c2f55b2
11 changed files with 1179 additions and 201 deletions
@@ -16,12 +16,14 @@
// limitations under the License.
use frame_support_procedural_tools::syn_ext as ext;
use proc_macro2::Span;
use proc_macro2::{Span, TokenStream};
use std::collections::HashSet;
use syn::{
ext::IdentExt,
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
token, Error, Ident, Result, Token,
token, Error, Ident, Path, PathArguments, PathSegment, Result, Token,
};
mod keyword {
@@ -154,7 +156,7 @@ pub struct PalletDeclaration {
pub name: Ident,
/// Optional fixed index (e.g. `MyPallet ... = 3,`)
pub index: Option<u8>,
pub pallet: Ident,
pub pallet: PalletPath,
pub instance: Option<Ident>,
pub pallet_parts: Vec<PalletPart>,
}
@@ -164,17 +166,16 @@ impl Parse for PalletDeclaration {
let name = input.parse()?;
let _: Token![:] = input.parse()?;
let pallet = input.parse()?;
let instance = if input.peek(Token![::]) && input.peek3(Token![<]) {
let _: Token![::] = input.parse()?;
let instance = if input.peek(Token![<]) {
let _: Token![<] = input.parse()?;
let res = Some(input.parse()?);
let _: Token![>] = input.parse()?;
let _: Token![::] = input.parse()?;
res
} else {
None
};
let _: Token![::] = input.parse()?;
let pallet_parts = parse_pallet_parts(input)?;
let index = if input.peek(Token![=]) {
@@ -198,6 +199,84 @@ impl Parse for PalletDeclaration {
}
}
/// A struct representing a path to a pallet. `PalletPath` is almost identical to the standard
/// Rust path with a few restrictions:
/// - No leading colons allowed
/// - Path segments can only consist of identifers; angle-bracketed or parenthesized segments will
/// result in a parsing error (except when specifying instances)
#[derive(Debug, Clone)]
pub struct PalletPath {
pub inner: Path,
}
impl Parse for PalletPath {
fn parse(input: ParseStream) -> Result<Self> {
let mut lookahead = input.lookahead1();
let mut segments = Punctuated::new();
if lookahead.peek(Token![crate])
|| lookahead.peek(Token![self])
|| lookahead.peek(Token![super])
|| lookahead.peek(Ident)
{
let ident = input.call(Ident::parse_any)?;
segments.push(PathSegment { ident, arguments: PathArguments::None });
let _: Token![::] = input.parse()?;
lookahead = input.lookahead1();
} else {
return Err(lookahead.error());
}
while lookahead.peek(Ident) {
let ident = input.parse()?;
segments.push(PathSegment { ident, arguments: PathArguments::None });
let _: Token![::] = input.parse()?;
lookahead = input.lookahead1();
}
if !lookahead.peek(token::Brace) && !lookahead.peek(Token![<]) {
return Err(lookahead.error());
}
Ok(Self {
inner: Path {
leading_colon: None,
segments,
}
})
}
}
impl PalletPath {
/// Return the snake-cased module name for this path.
pub fn mod_name(&self) -> Ident {
let mut iter = self.inner.segments.iter();
let mut mod_name = match &iter.next().expect("Path should always have 1 segment; qed").ident {
ident if ident == "self" || ident == "super" || ident == "crate" => {
// Skip `crate`, `self` and `super` quasi-keywords when creating the module name
iter.next()
.expect("There must be a path segment pointing to a pallet following \
`crate`, `self` or `super`; qed")
.ident
.clone()
}
ident => ident.clone(),
};
for segment in iter {
mod_name = quote::format_ident!("{}_{}", mod_name, segment.ident);
}
mod_name
}
}
impl quote::ToTokens for PalletPath {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.inner.to_tokens(tokens);
}
}
/// Parse [`PalletPart`]'s from a braces enclosed list that is split by commas, e.g.
///
/// `{ Call, Event }`
@@ -271,11 +350,6 @@ impl PalletPartKeyword {
}
}
/// Returns the name as `Ident`.
fn ident(&self) -> Ident {
Ident::new(self.name(), self.span())
}
/// Returns `true` if this pallet part is allowed to have generic arguments.
fn allows_generic(&self) -> bool {
Self::all_generic_arg().iter().any(|n| *n == self.name())
@@ -341,11 +415,6 @@ impl PalletPart {
pub fn name(&self) -> &'static str {
self.keyword.name()
}
/// The name of this pallet part as `Ident`.
pub fn ident(&self) -> Ident {
self.keyword.ident()
}
}
fn remove_kind(