From e4f3e85585689b5fbcd4491fd02f64f0ecb60368 Mon Sep 17 00:00:00 2001
From: thiolliere <gui.thiolliere@gmail.com>
Date: Fri, 24 Jan 2020 20:04:11 +0100
Subject: [PATCH] Improve decl storage parsing (#4731)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* improve decl storage parsing

* remove hidding detail macro

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
---
 .../support/procedural/src/storage/parse.rs   | 128 ++++++++++++++++--
 .../procedural/tools/derive/src/lib.rs        |  74 +---------
 .../support/procedural/tools/src/syn_ext.rs   |  33 -----
 3 files changed, 116 insertions(+), 119 deletions(-)

diff --git a/substrate/frame/support/procedural/src/storage/parse.rs b/substrate/frame/support/procedural/src/storage/parse.rs
index 5d91fbcc0e9..04af85a312e 100644
--- a/substrate/frame/support/procedural/src/storage/parse.rs
+++ b/substrate/frame/support/procedural/src/storage/parse.rs
@@ -38,10 +38,37 @@ mod keyword {
 	syn::custom_keyword!(hasher);
 }
 
+/// Specific `Opt` to implement structure with optional parsing
+#[derive(Debug, Clone)]
+pub struct Opt<P> {
+	pub inner: Option<P>,
+}
+impl<P: syn::export::ToTokens> syn::export::ToTokens for Opt<P> {
+	fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+		if let Some(ref p) = self.inner {
+			p.to_tokens(tokens);
+		}
+	}
+}
+
+macro_rules! impl_parse_for_opt {
+	($struct:ident => $token:path) => {
+		impl syn::parse::Parse for Opt<$struct> {
+			fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
+				if input.peek($token) {
+					input.parse().map(|p| Opt { inner: Some(p) })
+				} else {
+					Ok(Opt { inner: None })
+				}
+			}
+		}
+	};
+}
+
 /// Parsing usage only
 #[derive(Parse, ToTokens, Debug)]
 struct StorageDefinition {
-	pub hidden_crate: ext::Opt<SpecificHiddenCrate>,
+	pub hidden_crate: Opt<SpecificHiddenCrate>,
 	pub visibility: syn::Visibility,
 	pub trait_token: Token![trait],
 	pub ident: Ident,
@@ -62,7 +89,7 @@ struct StorageDefinition {
 	pub crate_ident: Ident,
 	pub where_clause: Option<syn::WhereClause>,
 	pub content: ext::Braces<ext::Punctuated<DeclStorageLine, Token![;]>>,
-	pub extra_genesis: ext::Opt<AddExtraGenesis>,
+	pub extra_genesis: Opt<AddExtraGenesis>,
 }
 
 #[derive(Parse, ToTokens, Debug)]
@@ -70,6 +97,7 @@ struct SpecificHiddenCrate {
 	pub keyword: keyword::hiddencrate,
 	pub ident: ext::Parens<Ident>,
 }
+impl_parse_for_opt!(SpecificHiddenCrate => keyword::hiddencrate);
 
 #[derive(Parse, ToTokens, Debug)]
 struct AddExtraGenesis {
@@ -77,17 +105,36 @@ struct AddExtraGenesis {
 	pub content: ext::Braces<AddExtraGenesisContent>,
 }
 
+impl_parse_for_opt!(AddExtraGenesis => keyword::add_extra_genesis);
+
 #[derive(Parse, ToTokens, Debug)]
 struct AddExtraGenesisContent {
 	pub lines: ext::Punctuated<AddExtraGenesisLineEnum, Token![;]>,
 }
 
-#[derive(Parse, ToTokens, Debug)]
+#[derive(ToTokens, Debug)]
 enum AddExtraGenesisLineEnum {
 	AddExtraGenesisLine(AddExtraGenesisLine),
 	AddExtraGenesisBuild(DeclStorageBuild),
 }
 
+impl syn::parse::Parse for AddExtraGenesisLineEnum {
+	fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
+		let input_fork = input.fork();
+		// OuterAttributes are forbidden for build variant,
+		// However to have better documentation we match against the keyword after those attributes.
+		let _: ext::OuterAttributes = input_fork.parse()?;
+		let lookahead = input_fork.lookahead1();
+		if lookahead.peek(keyword::build) {
+			Ok(Self::AddExtraGenesisBuild(input.parse()?))
+		} else if lookahead.peek(keyword::config) {
+			Ok(Self::AddExtraGenesisLine(input.parse()?))
+		} else {
+			Err(lookahead.error())
+		}
+	}
+}
+
 #[derive(Parse, ToTokens, Debug)]
 struct AddExtraGenesisLine {
 	pub attrs: ext::OuterAttributes,
@@ -95,7 +142,7 @@ struct AddExtraGenesisLine {
 	pub extra_field: ext::Parens<Ident>,
 	pub coldot_token: Token![:],
 	pub extra_type: syn::Type,
-	pub default_value: ext::Opt<DeclStorageDefault>,
+	pub default_value: Opt<DeclStorageDefault>,
 }
 
 #[derive(Parse, ToTokens, Debug)]
@@ -106,12 +153,12 @@ struct DeclStorageLine {
 	pub visibility: syn::Visibility,
 	// name
 	pub name: Ident,
-	pub getter: ext::Opt<DeclStorageGetter>,
-	pub config: ext::Opt<DeclStorageConfig>,
-	pub build: ext::Opt<DeclStorageBuild>,
+	pub getter: Opt<DeclStorageGetter>,
+	pub config: Opt<DeclStorageConfig>,
+	pub build: Opt<DeclStorageBuild>,
 	pub coldot_token: Token![:],
 	pub storage_type: DeclStorageType,
-	pub default_value: ext::Opt<DeclStorageDefault>,
+	pub default_value: Opt<DeclStorageDefault>,
 }
 
 #[derive(Parse, ToTokens, Debug)]
@@ -126,19 +173,25 @@ struct DeclStorageGetter {
 	pub getfn: ext::Parens<DeclStorageGetterBody>,
 }
 
+impl_parse_for_opt!(DeclStorageGetter => keyword::get);
+
 #[derive(Parse, ToTokens, Debug)]
 struct DeclStorageConfig {
 	pub config_keyword: keyword::config,
 	pub expr: ext::Parens<Option<syn::Ident>>,
 }
 
+impl_parse_for_opt!(DeclStorageConfig => keyword::config);
+
 #[derive(Parse, ToTokens, Debug)]
 struct DeclStorageBuild {
 	pub build_keyword: keyword::build,
 	pub expr: ext::Parens<syn::Expr>,
 }
 
-#[derive(Parse, ToTokens, Debug)]
+impl_parse_for_opt!(DeclStorageBuild => keyword::build);
+
+#[derive(ToTokens, Debug)]
 enum DeclStorageType {
 	Map(DeclStorageMap),
 	LinkedMap(DeclStorageLinkedMap),
@@ -146,10 +199,24 @@ enum DeclStorageType {
 	Simple(syn::Type),
 }
 
+impl syn::parse::Parse for DeclStorageType {
+	fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
+		if input.peek(keyword::map) {
+			Ok(Self::Map(input.parse()?))
+		} else if input.peek(keyword::linked_map) {
+			Ok(Self::LinkedMap(input.parse()?))
+		} else if input.peek(keyword::double_map) {
+			Ok(Self::DoubleMap(input.parse()?))
+		} else {
+			Ok(Self::Simple(input.parse()?))
+		}
+	}
+}
+
 #[derive(Parse, ToTokens, Debug)]
 struct DeclStorageMap {
 	pub map_keyword: keyword::map,
-	pub hasher: ext::Opt<SetHasher>,
+	pub hasher: Opt<SetHasher>,
 	pub key: syn::Type,
 	pub ass_keyword: Token![=>],
 	pub value: syn::Type,
@@ -158,7 +225,7 @@ struct DeclStorageMap {
 #[derive(Parse, ToTokens, Debug)]
 struct DeclStorageLinkedMap {
 	pub map_keyword: keyword::linked_map,
-	pub hasher: ext::Opt<SetHasher>,
+	pub hasher: Opt<SetHasher>,
 	pub key: syn::Type,
 	pub ass_keyword: Token![=>],
 	pub value: syn::Type,
@@ -167,16 +234,16 @@ struct DeclStorageLinkedMap {
 #[derive(Parse, ToTokens, Debug)]
 struct DeclStorageDoubleMap {
 	pub map_keyword: keyword::double_map,
-	pub hasher1: ext::Opt<SetHasher>,
+	pub hasher1: Opt<SetHasher>,
 	pub key1: syn::Type,
 	pub comma_keyword: Token![,],
-	pub hasher2: ext::Opt<SetHasher>,
+	pub hasher2: Opt<SetHasher>,
 	pub key2: syn::Type,
 	pub ass_keyword: Token![=>],
 	pub value: syn::Type,
 }
 
-#[derive(Parse, ToTokens, Debug)]
+#[derive(ToTokens, Debug)]
 enum Hasher {
 	Blake2_256(keyword::blake2_256),
 	Blake2_128(keyword::blake2_128),
@@ -186,18 +253,51 @@ enum Hasher {
 	Twox64Concat(keyword::twox_64_concat),
 }
 
+impl syn::parse::Parse for Hasher {
+	fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
+		let lookahead = input.lookahead1();
+		if lookahead.peek(keyword::blake2_256) {
+			Ok(Self::Blake2_256(input.parse()?))
+		} else if lookahead.peek(keyword::blake2_128) {
+			Ok(Self::Blake2_128(input.parse()?))
+		} else if lookahead.peek(keyword::blake2_128_concat) {
+			Ok(Self::Blake2_128Concat(input.parse()?))
+		} else if lookahead.peek(keyword::twox_256) {
+			Ok(Self::Twox256(input.parse()?))
+		} else if lookahead.peek(keyword::twox_128) {
+			Ok(Self::Twox128(input.parse()?))
+		} else if lookahead.peek(keyword::twox_64_concat) {
+			Ok(Self::Twox64Concat(input.parse()?))
+		} else {
+			Err(lookahead.error())
+		}
+	}
+}
+
 #[derive(Parse, ToTokens, Debug)]
 struct DeclStorageDefault {
 	pub equal_token: Token![=],
 	pub expr: syn::Expr,
 }
 
+impl syn::parse::Parse for Opt<DeclStorageDefault> {
+	fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
+		if input.peek(Token![=]) {
+			input.parse().map(|p| Opt { inner: Some(p) })
+		} else {
+			Ok(Opt { inner: None })
+		}
+	}
+}
+
 #[derive(Parse, ToTokens, Debug)]
 struct SetHasher {
 	pub hasher_keyword: keyword::hasher,
 	pub inner: ext::Parens<Hasher>,
 }
 
+impl_parse_for_opt!(SetHasher => keyword::hasher);
+
 impl From<SetHasher> for super::HasherKind {
 	fn from(set_hasher: SetHasher) -> Self {
 		set_hasher.inner.content.into()
diff --git a/substrate/frame/support/procedural/tools/derive/src/lib.rs b/substrate/frame/support/procedural/tools/derive/src/lib.rs
index e130ffbfa0e..f020c2a2fad 100644
--- a/substrate/frame/support/procedural/tools/derive/src/lib.rs
+++ b/substrate/frame/support/procedural/tools/derive/src/lib.rs
@@ -52,18 +52,13 @@ pub(crate) fn fields_access(
 	})
 }
 
-/// self defined parsing struct or enum.
-/// not meant for any struct/enum, just for fast
+/// self defined parsing struct.
+/// not meant for any struct, just for fast
 /// parse implementation.
-/// For enums:
-///   variant are tested in order of definition.
-///   Empty variant is always true.
-///   Please use carefully, this will fully parse successful variant twice.
 #[proc_macro_derive(Parse)]
 pub fn derive_parse(input: TokenStream) -> TokenStream {
 	let item = parse_macro_input!(input as syn::Item);
 	match item {
-		syn::Item::Enum(input) => derive_parse_enum(input),
 		syn::Item::Struct(input) => derive_parse_struct(input),
 		_ => TokenStream::new(), // ignore
 	}
@@ -100,71 +95,6 @@ fn derive_parse_struct(input: syn::ItemStruct) -> TokenStream {
 	tokens.into()
 }
 
-fn derive_parse_enum(input: syn::ItemEnum) -> TokenStream {
-	let syn::ItemEnum {
-		ident,
-		generics,
-		variants,
-		..
-	} = input;
-	let variants = variants.iter().map(|v| {
-		let variant_ident = v.ident.clone();
-		let fields_build = if v.fields.iter().count() > 0 {
-			let fields_id = fields_idents(v.fields.iter().map(Clone::clone));
-			quote!( (#(#fields_id), *) )
-		} else {
-			quote!()
-		};
-
-		let fields_procs = fields_idents(v.fields.iter().map(Clone::clone))
-			.map(|fident| {
-				quote!{
-					let mut #fident = match fork.parse() {
-						Ok(r) => r,
-						Err(_e) => break,
-					};
-				}
-			});
-		let fields_procs_again = fields_idents(v.fields.iter().map(Clone::clone))
-			.map(|fident| {
-				quote!{
-					#fident = input.parse().expect("was parsed just before");
-				}
-			});
-
-		// double parse to update input cursor position
-		// next syn crate version should be checked for a way
-		// to copy position/state from a fork
-		quote!{
-			let mut fork = input.fork();
-			loop {
-				#(#fields_procs)*
-				#(#fields_procs_again)*
-				return Ok(#ident::#variant_ident#fields_build);
-			}
-		}
-	});
-
-	let tokens = quote! {
-		impl #generics syn::parse::Parse for #ident #generics {
-			fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
-				#(
-					#variants
-				)*
-				// no early return from any variants
-				Err(
-					syn::parse::Error::new(
-						proc_macro2::Span::call_site(),
-						"derived enum no matching variants"
-					)
-				)
-			}
-		}
-
-	};
-	tokens.into()
-}
-
 /// self defined parsing struct or enum.
 /// not meant for any struct/enum, just for fast
 /// parse implementation.
diff --git a/substrate/frame/support/procedural/tools/src/syn_ext.rs b/substrate/frame/support/procedural/tools/src/syn_ext.rs
index d0a5066b801..7b0a2cb93fa 100644
--- a/substrate/frame/support/procedural/tools/src/syn_ext.rs
+++ b/substrate/frame/support/procedural/tools/src/syn_ext.rs
@@ -164,39 +164,6 @@ impl ToTokens for OuterAttributes {
 	}
 }
 
-#[derive(Debug)]
-pub struct Opt<P> {
-	pub inner: Option<P>,
-}
-
-impl<P: Parse> Parse for Opt<P> {
-	// Note that it cost a double parsing (same as enum derive)
-	fn parse(input: ParseStream) -> Result<Self> {
-		let inner = match input.fork().parse::<P>() {
-			Ok(_item) => Some(input.parse().expect("Same parsing ran before")),
-			Err(_e) => None,
-		};
-
-		Ok(Opt { inner })
-	}
-}
-
-impl<P: ToTokens> ToTokens for Opt<P> {
-	fn to_tokens(&self, tokens: &mut TokenStream) {
-		if let Some(ref p) = self.inner {
-			p.to_tokens(tokens);
-		}
-	}
-}
-
-impl <P: Clone> Clone for Opt<P> {
-	fn clone(&self) -> Self {
-		Self {
-			inner: self.inner.clone()
-		}
-	}
-}
-
 pub fn extract_type_option(typ: &syn::Type) -> Option<syn::Type> {
 	if let syn::Type::Path(ref path) = typ {
 		let v = path.path.segments.last()?;
-- 
GitLab