referrerpolicy=no-referrer-when-downgrade

frame_support_procedural_tools/
syn_ext.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
18// tag::description[]
19//! Extension to syn types, mainly for parsing
20// end::description[]
21
22use frame_support_procedural_tools_derive::{Parse, ToTokens};
23use proc_macro2::{TokenStream, TokenTree};
24use quote::ToTokens;
25use std::iter::once;
26use syn::{
27	parse::{Parse, ParseStream, Result},
28	visit::{self, Visit},
29	Ident,
30};
31
32/// stop parsing here getting remaining token as content
33/// Warn duplicate stream (part of)
34#[derive(Parse, ToTokens, Debug)]
35pub struct StopParse {
36	pub inner: TokenStream,
37}
38
39// inner macro really dependant on syn naming convention, do not export
40macro_rules! groups_impl {
41	($name:ident, $tok:ident, $deli:ident, $parse:ident) => {
42		#[derive(Debug)]
43		pub struct $name<P> {
44			pub token: syn::token::$tok,
45			pub content: P,
46		}
47
48		impl<P: Parse> Parse for $name<P> {
49			fn parse(input: ParseStream) -> Result<Self> {
50				let content;
51				let token = syn::$parse!(content in input);
52				let content = content.parse()?;
53				Ok($name { token, content })
54			}
55		}
56
57		impl<P: ToTokens> ToTokens for $name<P> {
58			fn to_tokens(&self, tokens: &mut TokenStream) {
59				let mut inner_stream = TokenStream::new();
60				self.content.to_tokens(&mut inner_stream);
61				let token_tree: proc_macro2::TokenTree =
62					proc_macro2::Group::new(proc_macro2::Delimiter::$deli, inner_stream).into();
63				tokens.extend(once(token_tree));
64			}
65		}
66
67		impl<P: Clone> Clone for $name<P> {
68			fn clone(&self) -> Self {
69				Self { token: self.token.clone(), content: self.content.clone() }
70			}
71		}
72	};
73}
74
75groups_impl!(Braces, Brace, Brace, braced);
76groups_impl!(Brackets, Bracket, Bracket, bracketed);
77groups_impl!(Parens, Paren, Parenthesis, parenthesized);
78
79#[derive(Debug)]
80pub struct PunctuatedInner<P, T, V> {
81	pub inner: syn::punctuated::Punctuated<P, T>,
82	pub variant: V,
83}
84
85#[derive(Debug, Clone)]
86pub struct NoTrailing;
87
88#[derive(Debug, Clone)]
89pub struct Trailing;
90
91pub type Punctuated<P, T> = PunctuatedInner<P, T, NoTrailing>;
92
93pub type PunctuatedTrailing<P, T> = PunctuatedInner<P, T, Trailing>;
94
95impl<P: Parse, T: Parse + syn::token::Token> Parse for PunctuatedInner<P, T, Trailing> {
96	fn parse(input: ParseStream) -> Result<Self> {
97		Ok(PunctuatedInner {
98			inner: syn::punctuated::Punctuated::parse_separated_nonempty(input)?,
99			variant: Trailing,
100		})
101	}
102}
103
104impl<P: Parse, T: Parse> Parse for PunctuatedInner<P, T, NoTrailing> {
105	fn parse(input: ParseStream) -> Result<Self> {
106		Ok(PunctuatedInner {
107			inner: syn::punctuated::Punctuated::parse_terminated(input)?,
108			variant: NoTrailing,
109		})
110	}
111}
112
113impl<P: ToTokens, T: ToTokens, V> ToTokens for PunctuatedInner<P, T, V> {
114	fn to_tokens(&self, tokens: &mut TokenStream) {
115		self.inner.to_tokens(tokens)
116	}
117}
118
119impl<P: Clone, T: Clone, V: Clone> Clone for PunctuatedInner<P, T, V> {
120	fn clone(&self) -> Self {
121		Self { inner: self.inner.clone(), variant: self.variant.clone() }
122	}
123}
124
125/// Note that syn Meta is almost fine for use case (lacks only `ToToken`)
126#[derive(Debug, Clone)]
127pub struct Meta {
128	pub inner: syn::Meta,
129}
130
131impl Parse for Meta {
132	fn parse(input: ParseStream) -> Result<Self> {
133		Ok(Meta { inner: syn::Meta::parse(input)? })
134	}
135}
136
137impl ToTokens for Meta {
138	fn to_tokens(&self, tokens: &mut TokenStream) {
139		match self.inner {
140			syn::Meta::Path(ref path) => path.to_tokens(tokens),
141			syn::Meta::List(ref l) => l.to_tokens(tokens),
142			syn::Meta::NameValue(ref n) => n.to_tokens(tokens),
143		}
144	}
145}
146
147#[derive(Debug)]
148pub struct OuterAttributes {
149	pub inner: Vec<syn::Attribute>,
150}
151
152impl Parse for OuterAttributes {
153	fn parse(input: ParseStream) -> Result<Self> {
154		let inner = syn::Attribute::parse_outer(input)?;
155		Ok(OuterAttributes { inner })
156	}
157}
158
159impl ToTokens for OuterAttributes {
160	fn to_tokens(&self, tokens: &mut TokenStream) {
161		for att in self.inner.iter() {
162			att.to_tokens(tokens);
163		}
164	}
165}
166
167pub fn extract_type_option(typ: &syn::Type) -> Option<syn::Type> {
168	if let syn::Type::Path(ref path) = typ {
169		let v = path.path.segments.last()?;
170		if v.ident == "Option" {
171			// Option has only one type argument in angle bracket.
172			if let syn::PathArguments::AngleBracketed(a) = &v.arguments {
173				if let syn::GenericArgument::Type(typ) = a.args.last()? {
174					return Some(typ.clone())
175				}
176			}
177		}
178	}
179
180	None
181}
182
183/// Auxiliary structure to check if a given `Ident` is contained in an ast.
184struct ContainsIdent<'a> {
185	ident: &'a Ident,
186	result: bool,
187}
188
189impl<'ast> ContainsIdent<'ast> {
190	fn visit_tokenstream(&mut self, stream: TokenStream) {
191		stream.into_iter().for_each(|tt| match tt {
192			TokenTree::Ident(id) => self.visit_ident(&id),
193			TokenTree::Group(ref group) => self.visit_tokenstream(group.stream()),
194			_ => {},
195		})
196	}
197
198	fn visit_ident(&mut self, ident: &Ident) {
199		if ident == self.ident {
200			self.result = true;
201		}
202	}
203}
204
205impl<'ast> Visit<'ast> for ContainsIdent<'ast> {
206	fn visit_ident(&mut self, input: &'ast Ident) {
207		self.visit_ident(input);
208	}
209
210	fn visit_macro(&mut self, input: &'ast syn::Macro) {
211		self.visit_tokenstream(input.tokens.clone());
212		visit::visit_macro(self, input);
213	}
214}
215
216/// Check if a `Type` contains the given `Ident`.
217pub fn type_contains_ident(typ: &syn::Type, ident: &Ident) -> bool {
218	let mut visit = ContainsIdent { result: false, ident };
219
220	visit::visit_type(&mut visit, typ);
221	visit.result
222}
223
224/// Check if a `Expr` contains the given `Ident`.
225pub fn expr_contains_ident(expr: &syn::Expr, ident: &Ident) -> bool {
226	let mut visit = ContainsIdent { result: false, ident };
227
228	visit::visit_expr(&mut visit, expr);
229	visit.result
230}