Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 8e71a22

Browse files
committed
macro modification
1 parent 9dd12f9 commit 8e71a22

File tree

12 files changed

+358
-28
lines changed

12 files changed

+358
-28
lines changed

frame/support/procedural/src/construct_runtime/mod.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,79 @@ use frame_support_procedural_tools::{generate_crate_access, generate_hidden_incl
2222
use parse::{ModuleDeclaration, RuntimeDefinition, WhereSection};
2323
use proc_macro::TokenStream;
2424
use proc_macro2::TokenStream as TokenStream2;
25-
use quote::quote;
25+
use quote::{quote, quote_spanned};
2626
use syn::{Ident, Result, TypePath};
2727

2828
/// The fixed name of the system module.
2929
const SYSTEM_MODULE_NAME: &str = "System";
3030

3131
pub fn construct_runtime(input: TokenStream) -> TokenStream {
32+
let input_clone = input.clone().into();
3233
let definition = syn::parse_macro_input!(input as RuntimeDefinition);
34+
35+
if let Some(preprocess) = construct_runtime_preprocess(&definition, input_clone)
36+
.unwrap_or_else(|e| Some(e.to_compile_error()))
37+
{
38+
return preprocess.into()
39+
}
40+
3341
construct_runtime_parsed(definition)
3442
.unwrap_or_else(|e| e.to_compile_error())
3543
.into()
3644
}
3745

46+
fn construct_runtime_preprocess(
47+
def: &RuntimeDefinition,
48+
input_clone: TokenStream2,
49+
) -> Result<Option<TokenStream2>> {
50+
let mut auto_modules = vec![];
51+
for module in def.modules.content.inner.iter() {
52+
if module.module_parts.is_none() {
53+
auto_modules.push(module.clone())
54+
}
55+
}
56+
57+
if !auto_modules.is_empty() {
58+
59+
// Make frame-support available to construct_runtime_args
60+
let hidden_crate_name = "construct_runtime_preprocess";
61+
let scrate_decl = generate_hidden_includes(&hidden_crate_name, "frame-support");
62+
let scrate = generate_crate_access(&hidden_crate_name, "frame-support");
63+
64+
let mut expand = quote!( #scrate::construct_runtime! { #input_clone } );
65+
66+
while let Some(module) = auto_modules.pop() {
67+
let macro_call = if def.local_macro.as_ref().map_or(false, |m| *m == module.module) {
68+
quote!( construct_runtime_args! )
69+
} else {
70+
let module = &module.module;
71+
quote!( #module::construct_runtime_args! )
72+
};
73+
74+
let module_name = &module.name;
75+
let module_module = &module.module;
76+
let module_instance = module.instance.as_ref()
77+
.map(|instance| quote!( ::< #instance > ));
78+
79+
expand = quote_spanned!(module_name.span() =>
80+
#macro_call {
81+
{ #module_name : #module_module #module_instance}
82+
#expand
83+
}
84+
);
85+
}
86+
87+
expand = quote!(
88+
#scrate_decl
89+
#expand
90+
);
91+
92+
Ok(Some(expand))
93+
} else {
94+
Ok(None)
95+
}
96+
}
97+
3898
fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result<TokenStream2> {
3999
let RuntimeDefinition {
40100
name,
@@ -164,6 +224,12 @@ fn decl_outer_inherent<'a>(
164224
})
165225
});
166226
quote!(
227+
// Prevent UncheckedExtrinsic to print unused warning.
228+
const _: () = {
229+
#[allow(unused)]
230+
type __hidden_use_unchecked_extrinsic = #unchecked_extrinsic;
231+
};
232+
167233
#scrate::impl_outer_inherent!(
168234
impl Inherents where Block = #block, UncheckedExtrinsic = #unchecked_extrinsic {
169235
#(#modules_tokens)*
@@ -201,6 +267,7 @@ fn decl_outer_config<'a>(
201267
#module #(#instance)* #(#generics)*,
202268
)
203269
});
270+
204271
quote!(
205272
#scrate::sp_runtime::impl_outer_config! {
206273
pub struct GenesisConfig for #runtime {
@@ -221,6 +288,7 @@ fn decl_runtime_metadata<'a>(
221288
module_declaration.find_part("Module").map(|_| {
222289
let filtered_names: Vec<_> = module_declaration
223290
.module_parts()
291+
.expect("Preprocessing has expanded module parts")
224292
.into_iter()
225293
.filter(|part| part.name() != "Module")
226294
.map(|part| part.ident())

frame/support/procedural/src/construct_runtime/parse.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,44 @@ mod keyword {
3636
syn::custom_keyword!(Origin);
3737
syn::custom_keyword!(Inherent);
3838
syn::custom_keyword!(ValidateUnsigned);
39+
syn::custom_keyword!(local_macro);
3940
}
4041

4142
#[derive(Debug)]
4243
pub struct RuntimeDefinition {
44+
// Specified through optional inner attribute: `#[local_macro(my_pallet)]`
45+
pub local_macro: Option<Ident>,
4346
pub visibility_token: Token![pub],
4447
pub enum_token: Token![enum],
4548
pub name: Ident,
4649
pub where_section: WhereSection,
4750
pub modules: ext::Braces<ext::Punctuated<ModuleDeclaration, Token![,]>>,
4851
}
4952

53+
pub struct LocalMacroDef(Ident);
54+
impl Parse for LocalMacroDef {
55+
fn parse(input: ParseStream) -> Result<Self> {
56+
input.parse::<Token![#]>()?;
57+
let attr;
58+
syn::bracketed!(attr in input);
59+
attr.parse::<keyword::local_macro>()?;
60+
let pallet;
61+
syn::parenthesized!(pallet in attr);
62+
let pallet = pallet.parse()?;
63+
Ok(Self(pallet))
64+
}
65+
}
66+
5067
impl Parse for RuntimeDefinition {
5168
fn parse(input: ParseStream) -> Result<Self> {
69+
let local_macro = if input.peek(Token![#]) {
70+
Some(input.parse::<LocalMacroDef>()?.0)
71+
} else {
72+
None
73+
};
74+
5275
Ok(Self {
76+
local_macro,
5377
visibility_token: input.parse()?,
5478
enum_token: input.parse()?,
5579
name: input.parse()?,
@@ -149,12 +173,12 @@ impl Parse for WhereDefinition {
149173
}
150174
}
151175

152-
#[derive(Debug)]
176+
#[derive(Debug, Clone)]
153177
pub struct ModuleDeclaration {
154178
pub name: Ident,
155179
pub module: Ident,
156180
pub instance: Option<Ident>,
157-
pub module_parts: Vec<ModulePart>,
181+
pub module_parts: Option<Vec<ModulePart>>,
158182
}
159183

160184
impl Parse for ModuleDeclaration {
@@ -172,8 +196,12 @@ impl Parse for ModuleDeclaration {
172196
None
173197
};
174198

175-
let _: Token![::] = input.parse()?;
176-
let module_parts = parse_module_parts(input)?;
199+
let module_parts = if input.peek(Token![::]) {
200+
let _: Token![::] = input.parse()?;
201+
Some(parse_module_parts(input)?)
202+
} else {
203+
None
204+
};
177205

178206
let parsed = Self {
179207
name,
@@ -188,12 +216,12 @@ impl Parse for ModuleDeclaration {
188216

189217
impl ModuleDeclaration {
190218
/// Get resolved module parts
191-
pub fn module_parts(&self) -> &[ModulePart] {
192-
&self.module_parts
219+
pub fn module_parts(&self) -> Option<&Vec<ModulePart>> {
220+
self.module_parts.as_ref()
193221
}
194222

195223
pub fn find_part(&self, name: &str) -> Option<&ModulePart> {
196-
self.module_parts.iter().find(|part| part.name() == name)
224+
self.module_parts.as_ref().and_then(|p| p.iter().find(|part| part.name() == name))
197225
}
198226

199227
pub fn exists_part(&self, name: &str) -> bool {
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use proc_macro2::{TokenStream, TokenTree, Group, Span};
2+
use syn::spanned::Spanned;
3+
use std::iter::once;
4+
5+
pub fn expand_after(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
6+
let def = syn::parse_macro_input!(input as ExpandAfterDef);
7+
let expand_in_span = def.expand_in.span();
8+
9+
match expand_in_stream(&def.expand_after, &mut Some(def.expand_with), def.expand_in) {
10+
Ok(stream) => stream.into(),
11+
Err(_) => {
12+
let msg = format!("Cannot find pattern `{:?}` in given token stream", def.expand_after);
13+
syn::Error::new(expand_in_span, msg).to_compile_error().into()
14+
},
15+
}
16+
}
17+
18+
struct ExpandAfterDef {
19+
// This is ensured to have no TokenTree::Group nor TokenTree::Literal and not being empty.
20+
expand_after: Vec<TokenTree>,
21+
expand_with: TokenStream,
22+
expand_in: TokenStream,
23+
}
24+
25+
impl syn::parse::Parse for ExpandAfterDef {
26+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
27+
let expand_after;
28+
let _replace_with_bracket: syn::token::Brace = syn::braced!(expand_after in input);
29+
let expand_after = expand_after.parse::<TokenStream>()?
30+
.into_iter().collect::<Vec<TokenTree>>();
31+
32+
if let Some(t) = expand_after.iter().find(|t| matches!(t, TokenTree::Group(_))) {
33+
return Err(syn::Error::new(t.span(), "Unexpected group token tree"));
34+
}
35+
if let Some(t) = expand_after.iter().find(|t| matches!(t, TokenTree::Literal(_))) {
36+
return Err(syn::Error::new(t.span(), "Unexpected literal token tree"));
37+
}
38+
39+
if expand_after.is_empty() {
40+
return Err(syn::Error::new(Span::call_site(), "empty match pattern is invalid"));
41+
}
42+
43+
let expand_with;
44+
let _replace_with_bracket: syn::token::Brace = syn::braced!(expand_with in input);
45+
let expand_with: TokenStream = expand_with.parse()?;
46+
47+
Ok(Self {
48+
expand_with,
49+
expand_after,
50+
expand_in: input.parse()?,
51+
})
52+
}
53+
}
54+
55+
// Replace the first found `auto` ident by content of `with`.
56+
// `with` must be some (Option is used for internal simplification).
57+
// `after` musn't be empty and only contains Ident or Punct
58+
fn expand_in_stream(
59+
after: &Vec<TokenTree>,
60+
with: &mut Option<TokenStream>,
61+
stream: TokenStream
62+
) -> Result<TokenStream, ()> {
63+
assert!(with.is_some(), "`with` must be some, Option is used because `with` is used only once");
64+
assert!(!after.is_empty(), "`after` mustn't be empty, otherwise it cannot be found");
65+
66+
let mut stream = stream.into_iter();
67+
let mut extended = TokenStream::new();
68+
let mut match_cursor = 0;
69+
70+
loop {
71+
match stream.next() {
72+
Some(TokenTree::Group(group)) => {
73+
match_cursor = 0;
74+
let stream = group.stream();
75+
match expand_in_stream(after, with, stream) {
76+
Ok(stream) => {
77+
extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), stream))));
78+
break;
79+
}
80+
Err(_) => {
81+
extended.extend(once(TokenTree::Group(group)));
82+
}
83+
}
84+
}
85+
Some(other) => {
86+
use TokenTree::{Ident, Punct};
87+
88+
let other_match_pattern = match (&other, &after[match_cursor]) {
89+
(Ident(i1), Ident(i2)) if i1 == i2 => true,
90+
(Punct(p1), Punct(p2)) if p1.as_char() == p2.as_char() => true,
91+
_ => false,
92+
};
93+
94+
if other_match_pattern {
95+
match_cursor += 1
96+
}
97+
98+
extended.extend(once(other));
99+
100+
if match_cursor == after.len() {
101+
extended.extend(once(with.take().expect("with is used to replace only once")));
102+
break;
103+
}
104+
}
105+
None => return Err(())
106+
}
107+
}
108+
109+
extended.extend(stream);
110+
111+
Ok(extended)
112+
}

0 commit comments

Comments
 (0)