frame_support_procedural/pallet/parse/
mod.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//! Parse for pallet macro.
19//!
20//! Parse the module into `Def` struct through `Def::try_from` function.
21
22pub mod call;
23pub mod composite;
24pub mod config;
25pub mod error;
26pub mod event;
27pub mod extra_constants;
28pub mod genesis_build;
29pub mod genesis_config;
30pub mod helper;
31pub mod hooks;
32pub mod inherent;
33pub mod origin;
34pub mod pallet_struct;
35pub mod storage;
36pub mod tasks;
37pub mod type_value;
38pub mod validate_unsigned;
39
40#[cfg(test)]
41pub mod tests;
42
43use composite::{keyword::CompositeKeyword, CompositeDef};
44use frame_support_procedural_tools::generate_access_from_frame_or_crate;
45use quote::ToTokens;
46use syn::spanned::Spanned;
47
48/// Parsed definition of a pallet.
49pub struct Def {
50	/// The module items.
51	/// (their order must not be modified because they are registered in individual definitions).
52	pub item: syn::ItemMod,
53	pub config: config::ConfigDef,
54	pub pallet_struct: pallet_struct::PalletStructDef,
55	pub hooks: Option<hooks::HooksDef>,
56	pub call: Option<call::CallDef>,
57	pub tasks: Option<tasks::TasksDef>,
58	pub task_enum: Option<tasks::TaskEnumDef>,
59	pub storages: Vec<storage::StorageDef>,
60	pub error: Option<error::ErrorDef>,
61	pub event: Option<event::EventDef>,
62	pub origin: Option<origin::OriginDef>,
63	pub inherent: Option<inherent::InherentDef>,
64	pub genesis_config: Option<genesis_config::GenesisConfigDef>,
65	pub genesis_build: Option<genesis_build::GenesisBuildDef>,
66	pub validate_unsigned: Option<validate_unsigned::ValidateUnsignedDef>,
67	pub extra_constants: Option<extra_constants::ExtraConstantsDef>,
68	pub composites: Vec<composite::CompositeDef>,
69	pub type_values: Vec<type_value::TypeValueDef>,
70	pub frame_system: syn::Path,
71	pub frame_support: syn::Path,
72	pub dev_mode: bool,
73}
74
75impl Def {
76	pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result<Self> {
77		let frame_system = generate_access_from_frame_or_crate("frame-system")?;
78		let frame_support = generate_access_from_frame_or_crate("frame-support")?;
79		let item_span = item.span();
80		let items = &mut item
81			.content
82			.as_mut()
83			.ok_or_else(|| {
84				let msg = "Invalid pallet definition, expected mod to be inlined.";
85				syn::Error::new(item_span, msg)
86			})?
87			.1;
88
89		let mut config = None;
90		let mut pallet_struct = None;
91		let mut hooks = None;
92		let mut call = None;
93		let mut tasks = None;
94		let mut task_enum = None;
95		let mut error = None;
96		let mut event = None;
97		let mut origin = None;
98		let mut inherent = None;
99		let mut genesis_config = None;
100		let mut genesis_build = None;
101		let mut validate_unsigned = None;
102		let mut extra_constants = None;
103		let mut storages = vec![];
104		let mut type_values = vec![];
105		let mut composites: Vec<CompositeDef> = vec![];
106
107		for (index, item) in items.iter_mut().enumerate() {
108			let pallet_attr: Option<PalletAttr> = helper::take_first_item_pallet_attr(item)?;
109
110			match pallet_attr {
111				Some(PalletAttr::Config(_, with_default)) if config.is_none() =>
112					config = Some(config::ConfigDef::try_from(
113						&frame_system,
114						index,
115						item,
116						with_default,
117					)?),
118				Some(PalletAttr::Pallet(span)) if pallet_struct.is_none() => {
119					let p = pallet_struct::PalletStructDef::try_from(span, index, item)?;
120					pallet_struct = Some(p);
121				},
122				Some(PalletAttr::Hooks(span)) if hooks.is_none() => {
123					let m = hooks::HooksDef::try_from(span, item)?;
124					hooks = Some(m);
125				},
126				Some(PalletAttr::RuntimeCall(cw, span)) if call.is_none() =>
127					call = Some(call::CallDef::try_from(span, index, item, dev_mode, cw)?),
128				Some(PalletAttr::Tasks(_)) if tasks.is_none() => {
129					let item_tokens = item.to_token_stream();
130					// `TasksDef::parse` needs to know if attr was provided so we artificially
131					// re-insert it here
132					tasks = Some(syn::parse2::<tasks::TasksDef>(quote::quote! {
133						#[pallet::tasks_experimental]
134						#item_tokens
135					})?);
136
137					// replace item with a no-op because it will be handled by the expansion of tasks
138					*item = syn::Item::Verbatim(quote::quote!());
139				}
140				Some(PalletAttr::TaskCondition(span)) => return Err(syn::Error::new(
141					span,
142					"`#[pallet::task_condition]` can only be used on items within an `impl` statement."
143				)),
144				Some(PalletAttr::TaskIndex(span)) => return Err(syn::Error::new(
145					span,
146					"`#[pallet::task_index]` can only be used on items within an `impl` statement."
147				)),
148				Some(PalletAttr::TaskList(span)) => return Err(syn::Error::new(
149					span,
150					"`#[pallet::task_list]` can only be used on items within an `impl` statement."
151				)),
152				Some(PalletAttr::RuntimeTask(_)) if task_enum.is_none() =>
153					task_enum = Some(syn::parse2::<tasks::TaskEnumDef>(item.to_token_stream())?),
154				Some(PalletAttr::Error(span)) if error.is_none() =>
155					error = Some(error::ErrorDef::try_from(span, index, item)?),
156				Some(PalletAttr::RuntimeEvent(span)) if event.is_none() =>
157					event = Some(event::EventDef::try_from(span, index, item)?),
158				Some(PalletAttr::GenesisConfig(_)) if genesis_config.is_none() => {
159					let g = genesis_config::GenesisConfigDef::try_from(index, item)?;
160					genesis_config = Some(g);
161				},
162				Some(PalletAttr::GenesisBuild(span)) if genesis_build.is_none() => {
163					let g = genesis_build::GenesisBuildDef::try_from(span, item)?;
164					genesis_build = Some(g);
165				},
166				Some(PalletAttr::RuntimeOrigin(_)) if origin.is_none() =>
167					origin = Some(origin::OriginDef::try_from(item)?),
168				Some(PalletAttr::Inherent(_)) if inherent.is_none() =>
169					inherent = Some(inherent::InherentDef::try_from(item)?),
170				Some(PalletAttr::Storage(span)) =>
171					storages.push(storage::StorageDef::try_from(span, index, item, dev_mode)?),
172				Some(PalletAttr::ValidateUnsigned(_)) if validate_unsigned.is_none() => {
173					let v = validate_unsigned::ValidateUnsignedDef::try_from(item)?;
174					validate_unsigned = Some(v);
175				},
176				Some(PalletAttr::TypeValue(span)) =>
177					type_values.push(type_value::TypeValueDef::try_from(span, index, item)?),
178				Some(PalletAttr::ExtraConstants(_)) =>
179					extra_constants =
180						Some(extra_constants::ExtraConstantsDef::try_from(item)?),
181				Some(PalletAttr::Composite(span)) => {
182					let composite =
183						composite::CompositeDef::try_from(span, &frame_support, item)?;
184					if composites.iter().any(|def| {
185						match (&def.composite_keyword, &composite.composite_keyword) {
186							(
187								CompositeKeyword::FreezeReason(_),
188								CompositeKeyword::FreezeReason(_),
189							) |
190							(CompositeKeyword::HoldReason(_), CompositeKeyword::HoldReason(_)) |
191							(CompositeKeyword::LockId(_), CompositeKeyword::LockId(_)) |
192							(
193								CompositeKeyword::SlashReason(_),
194								CompositeKeyword::SlashReason(_),
195							) => true,
196							_ => false,
197						}
198					}) {
199						let msg = format!(
200							"Invalid duplicated `{}` definition",
201							composite.composite_keyword
202						);
203						return Err(syn::Error::new(composite.composite_keyword.span(), &msg))
204					}
205					composites.push(composite);
206				},
207				Some(attr) => {
208					let msg = "Invalid duplicated attribute";
209					return Err(syn::Error::new(attr.span(), msg))
210				},
211				None => (),
212			}
213		}
214
215		if genesis_config.is_some() != genesis_build.is_some() {
216			let msg = format!(
217				"`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \
218				either both used or both not used, instead genesis_config is {} and genesis_build \
219				is {}",
220				genesis_config.as_ref().map_or("unused", |_| "used"),
221				genesis_build.as_ref().map_or("unused", |_| "used"),
222			);
223			return Err(syn::Error::new(item_span, msg));
224		}
225
226		Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?;
227
228		let def = Def {
229			item,
230			config: config
231				.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?,
232			pallet_struct: pallet_struct
233				.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?,
234			hooks,
235			call,
236			tasks,
237			task_enum,
238			extra_constants,
239			genesis_config,
240			genesis_build,
241			validate_unsigned,
242			error,
243			event,
244			origin,
245			inherent,
246			storages,
247			composites,
248			type_values,
249			frame_system,
250			frame_support,
251			dev_mode,
252		};
253
254		def.check_instance_usage()?;
255		def.check_event_usage()?;
256
257		Ok(def)
258	}
259
260	/// Performs extra logic checks necessary for the `#[pallet::tasks_experimental]` feature.
261	fn resolve_tasks(
262		item_span: &proc_macro2::Span,
263		tasks: &mut Option<tasks::TasksDef>,
264		task_enum: &mut Option<tasks::TaskEnumDef>,
265		items: &mut Vec<syn::Item>,
266	) -> syn::Result<()> {
267		// fallback for manual (without macros) definition of tasks impl
268		Self::resolve_manual_tasks_impl(tasks, task_enum, items)?;
269
270		// fallback for manual (without macros) definition of task enum
271		Self::resolve_manual_task_enum(tasks, task_enum, items)?;
272
273		// ensure that if `task_enum` is specified, `tasks` is also specified
274		match (&task_enum, &tasks) {
275			(Some(_), None) =>
276				return Err(syn::Error::new(
277					*item_span,
278					"Missing `#[pallet::tasks_experimental]` impl",
279				)),
280			(None, Some(tasks)) =>
281				if tasks.tasks_attr.is_none() {
282					return Err(syn::Error::new(
283						tasks.item_impl.impl_token.span(),
284						"A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \
285						task enum has been omitted",
286					));
287				} else {
288				},
289			_ => (),
290		}
291
292		Ok(())
293	}
294
295	/// Tries to locate task enum based on the tasks impl target if attribute is not specified
296	/// but impl is present. If one is found, `task_enum` is set appropriately.
297	fn resolve_manual_task_enum(
298		tasks: &Option<tasks::TasksDef>,
299		task_enum: &mut Option<tasks::TaskEnumDef>,
300		items: &mut Vec<syn::Item>,
301	) -> syn::Result<()> {
302		let (None, Some(tasks)) = (&task_enum, &tasks) else { return Ok(()) };
303		let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { return Ok(()) };
304		let type_path = type_path.path.segments.iter().collect::<Vec<_>>();
305		let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { return Ok(()) };
306		let mut result = None;
307		for item in items {
308			let syn::Item::Enum(item_enum) = item else { continue };
309			if item_enum.ident == seg.ident {
310				result = Some(syn::parse2::<tasks::TaskEnumDef>(item_enum.to_token_stream())?);
311				// replace item with a no-op because it will be handled by the expansion of
312				// `task_enum`. We use a no-op instead of simply removing it from the vec
313				// so that any indices collected by `Def::try_from` remain accurate
314				*item = syn::Item::Verbatim(quote::quote!());
315				break;
316			}
317		}
318		*task_enum = result;
319		Ok(())
320	}
321
322	/// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is
323	/// `Task`) in the event that one has not been found already via the attribute macro
324	pub fn resolve_manual_tasks_impl(
325		tasks: &mut Option<tasks::TasksDef>,
326		task_enum: &Option<tasks::TaskEnumDef>,
327		items: &Vec<syn::Item>,
328	) -> syn::Result<()> {
329		let None = tasks else { return Ok(()) };
330		let mut result = None;
331		for item in items {
332			let syn::Item::Impl(item_impl) = item else { continue };
333			let Some((_, path, _)) = &item_impl.trait_ else { continue };
334			let Some(trait_last_seg) = path.segments.last() else { continue };
335			let syn::Type::Path(target_path) = &*item_impl.self_ty else { continue };
336			let target_path = target_path.path.segments.iter().collect::<Vec<_>>();
337			let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else {
338				continue;
339			};
340			let matches_task_enum = match task_enum {
341				Some(task_enum) => task_enum.item_enum.ident == target_ident.ident,
342				None => true,
343			};
344			if trait_last_seg.ident == "Task" && matches_task_enum {
345				result = Some(syn::parse2::<tasks::TasksDef>(item_impl.to_token_stream())?);
346				break;
347			}
348		}
349		*tasks = result;
350		Ok(())
351	}
352
353	/// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared
354	/// and trait defines type RuntimeEvent, or not declared and no trait associated type.
355	fn check_event_usage(&self) -> syn::Result<()> {
356		match (self.config.has_event_type, self.event.is_some()) {
357			(true, false) => {
358				let msg = "Invalid usage of RuntimeEvent, `Config` contains associated type `RuntimeEvent`, \
359					but enum `Event` is not declared (i.e. no use of `#[pallet::event]`). \
360					Note that type `RuntimeEvent` in trait is reserved to work alongside pallet event.";
361				Err(syn::Error::new(proc_macro2::Span::call_site(), msg))
362			},
363			(false, true) => {
364				let msg = "Invalid usage of RuntimeEvent, `Config` contains no associated type \
365					`RuntimeEvent`, but enum `Event` is declared (in use of `#[pallet::event]`). \
366					An RuntimeEvent associated type must be declare on trait `Config`.";
367				Err(syn::Error::new(proc_macro2::Span::call_site(), msg))
368			},
369			_ => Ok(()),
370		}
371	}
372
373	/// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with
374	/// instance iff it is defined with instance.
375	fn check_instance_usage(&self) -> syn::Result<()> {
376		let mut instances = vec![];
377		instances.extend_from_slice(&self.pallet_struct.instances[..]);
378		instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone()));
379		if let Some(call) = &self.call {
380			instances.extend_from_slice(&call.instances[..]);
381		}
382		if let Some(hooks) = &self.hooks {
383			instances.extend_from_slice(&hooks.instances[..]);
384		}
385		if let Some(event) = &self.event {
386			instances.extend_from_slice(&event.instances[..]);
387		}
388		if let Some(error) = &self.error {
389			instances.extend_from_slice(&error.instances[..]);
390		}
391		if let Some(inherent) = &self.inherent {
392			instances.extend_from_slice(&inherent.instances[..]);
393		}
394		if let Some(origin) = &self.origin {
395			instances.extend_from_slice(&origin.instances[..]);
396		}
397		if let Some(genesis_config) = &self.genesis_config {
398			instances.extend_from_slice(&genesis_config.instances[..]);
399		}
400		if let Some(genesis_build) = &self.genesis_build {
401			genesis_build.instances.as_ref().map(|i| instances.extend_from_slice(&i));
402		}
403		if let Some(extra_constants) = &self.extra_constants {
404			instances.extend_from_slice(&extra_constants.instances[..]);
405		}
406
407		let mut errors = instances.into_iter().filter_map(|instances| {
408			if instances.has_instance == self.config.has_instance {
409				return None;
410			}
411			let msg = if self.config.has_instance {
412				"Invalid generic declaration, trait is defined with instance but generic use none"
413			} else {
414				"Invalid generic declaration, trait is defined without instance but generic use \
415						some"
416			};
417			Some(syn::Error::new(instances.span, msg))
418		});
419
420		if let Some(mut first_error) = errors.next() {
421			for error in errors {
422				first_error.combine(error)
423			}
424			Err(first_error)
425		} else {
426			Ok(())
427		}
428	}
429
430	/// Depending on if pallet is instantiable:
431	/// * either `T: Config`
432	/// * or `T: Config<I>, I: 'static`
433	pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
434		if self.config.has_instance {
435			quote::quote_spanned!(span => T: Config<I>, I: 'static)
436		} else {
437			quote::quote_spanned!(span => T: Config)
438		}
439	}
440
441	/// Depending on if pallet is instantiable:
442	/// * either `T: Config`
443	/// * or `T: Config<I>, I: 'static = ()`
444	pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
445		if self.config.has_instance {
446			quote::quote_spanned!(span => T: Config<I>, I: 'static = ())
447		} else {
448			quote::quote_spanned!(span => T: Config)
449		}
450	}
451
452	/// Depending on if pallet is instantiable:
453	/// * either `T`
454	/// * or `T, I = ()`
455	pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
456		if self.config.has_instance {
457			quote::quote_spanned!(span => T, I = ())
458		} else {
459			quote::quote_spanned!(span => T)
460		}
461	}
462
463	/// Depending on if pallet is instantiable:
464	/// * either ``
465	/// * or `<I>`
466	/// to be used when using pallet trait `Config`
467	pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
468		if self.config.has_instance {
469			quote::quote_spanned!(span => <I>)
470		} else {
471			quote::quote_spanned!(span => )
472		}
473	}
474
475	/// Depending on if pallet is instantiable:
476	/// * either `T`
477	/// * or `T, I`
478	pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
479		if self.config.has_instance {
480			quote::quote_spanned!(span => T, I)
481		} else {
482			quote::quote_spanned!(span => T)
483		}
484	}
485}
486
487/// Some generic kind for type which can be not generic, or generic over config,
488/// or generic over config and instance, but not generic only over instance.
489pub enum GenericKind {
490	None,
491	Config,
492	ConfigAndInstance,
493}
494
495impl GenericKind {
496	/// Return Err if it is only generics over instance but not over config.
497	pub fn from_gens(has_config: bool, has_instance: bool) -> Result<Self, ()> {
498		match (has_config, has_instance) {
499			(false, false) => Ok(GenericKind::None),
500			(true, false) => Ok(GenericKind::Config),
501			(true, true) => Ok(GenericKind::ConfigAndInstance),
502			(false, true) => Err(()),
503		}
504	}
505
506	/// Return the generic to be used when using the type.
507	///
508	/// Depending on its definition it can be: ``, `T` or `T, I`
509	pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
510		match self {
511			GenericKind::None => quote::quote!(),
512			GenericKind::Config => quote::quote_spanned!(span => T),
513			GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I),
514		}
515	}
516
517	/// Return the generic to be used in `impl<..>` when implementing on the type.
518	pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
519		match self {
520			GenericKind::None => quote::quote!(),
521			GenericKind::Config => quote::quote_spanned!(span => T: Config),
522			GenericKind::ConfigAndInstance => {
523				quote::quote_spanned!(span => T: Config<I>, I: 'static)
524			},
525		}
526	}
527
528	/// Return whereas the type has some generic.
529	pub fn is_generic(&self) -> bool {
530		match self {
531			GenericKind::None => false,
532			GenericKind::Config | GenericKind::ConfigAndInstance => true,
533		}
534	}
535}
536
537/// List of additional token to be used for parsing.
538mod keyword {
539	syn::custom_keyword!(origin);
540	syn::custom_keyword!(call);
541	syn::custom_keyword!(tasks_experimental);
542	syn::custom_keyword!(task_enum);
543	syn::custom_keyword!(task_list);
544	syn::custom_keyword!(task_condition);
545	syn::custom_keyword!(task_index);
546	syn::custom_keyword!(weight);
547	syn::custom_keyword!(event);
548	syn::custom_keyword!(config);
549	syn::custom_keyword!(with_default);
550	syn::custom_keyword!(hooks);
551	syn::custom_keyword!(inherent);
552	syn::custom_keyword!(error);
553	syn::custom_keyword!(storage);
554	syn::custom_keyword!(genesis_build);
555	syn::custom_keyword!(genesis_config);
556	syn::custom_keyword!(validate_unsigned);
557	syn::custom_keyword!(type_value);
558	syn::custom_keyword!(pallet);
559	syn::custom_keyword!(extra_constants);
560	syn::custom_keyword!(composite_enum);
561}
562
563/// Parse attributes for item in pallet module
564/// syntax must be `pallet::` (e.g. `#[pallet::config]`)
565enum PalletAttr {
566	Config(proc_macro2::Span, bool),
567	Pallet(proc_macro2::Span),
568	Hooks(proc_macro2::Span),
569	/// A `#[pallet::call]` with optional attributes to specialize the behaviour.
570	///
571	/// # Attributes
572	///
573	/// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or
574	/// `#[pallet::call(attr(…))]`. The possible attributes are:
575	///
576	/// ## `weight`
577	///
578	/// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one
579	/// argument that is expected to be an implementation of the `WeightInfo` or something that
580	/// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls.
581	/// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the
582	/// one from the `#[pallet::call]` definition. So instead of having to write it on each call:
583	///
584	/// ```ignore
585	/// #[pallet::call]
586	/// impl<T: Config> Pallet<T> {
587	///     #[pallet::weight(T::WeightInfo::create())]
588	///     pub fn create(
589	/// ```
590	/// you can now omit it on the call itself, if the name of the weigh function matches the call:
591	///
592	/// ```ignore
593	/// #[pallet::call(weight = <T as crate::Config>::WeightInfo)]
594	/// impl<T: Config> Pallet<T> {
595	///     pub fn create(
596	/// ```
597	///
598	/// It is possible to use this syntax together with instantiated pallets by using `Config<I>`
599	/// instead.
600	///
601	/// ### Dev Mode
602	///
603	/// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation
604	/// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used
605	/// instead of the zero weight. So to say: it works together with `dev_mode`.
606	RuntimeCall(Option<InheritedCallWeightAttr>, proc_macro2::Span),
607	Error(proc_macro2::Span),
608	Tasks(proc_macro2::Span),
609	TaskList(proc_macro2::Span),
610	TaskCondition(proc_macro2::Span),
611	TaskIndex(proc_macro2::Span),
612	RuntimeTask(proc_macro2::Span),
613	RuntimeEvent(proc_macro2::Span),
614	RuntimeOrigin(proc_macro2::Span),
615	Inherent(proc_macro2::Span),
616	Storage(proc_macro2::Span),
617	GenesisConfig(proc_macro2::Span),
618	GenesisBuild(proc_macro2::Span),
619	ValidateUnsigned(proc_macro2::Span),
620	TypeValue(proc_macro2::Span),
621	ExtraConstants(proc_macro2::Span),
622	Composite(proc_macro2::Span),
623}
624
625impl PalletAttr {
626	fn span(&self) -> proc_macro2::Span {
627		match self {
628			Self::Config(span, _) => *span,
629			Self::Pallet(span) => *span,
630			Self::Hooks(span) => *span,
631			Self::Tasks(span) => *span,
632			Self::TaskCondition(span) => *span,
633			Self::TaskIndex(span) => *span,
634			Self::TaskList(span) => *span,
635			Self::Error(span) => *span,
636			Self::RuntimeTask(span) => *span,
637			Self::RuntimeCall(_, span) => *span,
638			Self::RuntimeEvent(span) => *span,
639			Self::RuntimeOrigin(span) => *span,
640			Self::Inherent(span) => *span,
641			Self::Storage(span) => *span,
642			Self::GenesisConfig(span) => *span,
643			Self::GenesisBuild(span) => *span,
644			Self::ValidateUnsigned(span) => *span,
645			Self::TypeValue(span) => *span,
646			Self::ExtraConstants(span) => *span,
647			Self::Composite(span) => *span,
648		}
649	}
650}
651
652impl syn::parse::Parse for PalletAttr {
653	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
654		input.parse::<syn::Token![#]>()?;
655		let content;
656		syn::bracketed!(content in input);
657		content.parse::<keyword::pallet>()?;
658		content.parse::<syn::Token![::]>()?;
659
660		let lookahead = content.lookahead1();
661		if lookahead.peek(keyword::config) {
662			let span = content.parse::<keyword::config>()?.span();
663			let with_default = content.peek(syn::token::Paren);
664			if with_default {
665				let inside_config;
666				let _paren = syn::parenthesized!(inside_config in content);
667				inside_config.parse::<keyword::with_default>()?;
668			}
669			Ok(PalletAttr::Config(span, with_default))
670		} else if lookahead.peek(keyword::pallet) {
671			Ok(PalletAttr::Pallet(content.parse::<keyword::pallet>()?.span()))
672		} else if lookahead.peek(keyword::hooks) {
673			Ok(PalletAttr::Hooks(content.parse::<keyword::hooks>()?.span()))
674		} else if lookahead.peek(keyword::call) {
675			let span = content.parse::<keyword::call>().expect("peeked").span();
676			let attr = match content.is_empty() {
677				true => None,
678				false => Some(InheritedCallWeightAttr::parse(&content)?),
679			};
680			Ok(PalletAttr::RuntimeCall(attr, span))
681		} else if lookahead.peek(keyword::tasks_experimental) {
682			Ok(PalletAttr::Tasks(content.parse::<keyword::tasks_experimental>()?.span()))
683		} else if lookahead.peek(keyword::task_enum) {
684			Ok(PalletAttr::RuntimeTask(content.parse::<keyword::task_enum>()?.span()))
685		} else if lookahead.peek(keyword::task_condition) {
686			Ok(PalletAttr::TaskCondition(content.parse::<keyword::task_condition>()?.span()))
687		} else if lookahead.peek(keyword::task_index) {
688			Ok(PalletAttr::TaskIndex(content.parse::<keyword::task_index>()?.span()))
689		} else if lookahead.peek(keyword::task_list) {
690			Ok(PalletAttr::TaskList(content.parse::<keyword::task_list>()?.span()))
691		} else if lookahead.peek(keyword::error) {
692			Ok(PalletAttr::Error(content.parse::<keyword::error>()?.span()))
693		} else if lookahead.peek(keyword::event) {
694			Ok(PalletAttr::RuntimeEvent(content.parse::<keyword::event>()?.span()))
695		} else if lookahead.peek(keyword::origin) {
696			Ok(PalletAttr::RuntimeOrigin(content.parse::<keyword::origin>()?.span()))
697		} else if lookahead.peek(keyword::inherent) {
698			Ok(PalletAttr::Inherent(content.parse::<keyword::inherent>()?.span()))
699		} else if lookahead.peek(keyword::storage) {
700			Ok(PalletAttr::Storage(content.parse::<keyword::storage>()?.span()))
701		} else if lookahead.peek(keyword::genesis_config) {
702			Ok(PalletAttr::GenesisConfig(content.parse::<keyword::genesis_config>()?.span()))
703		} else if lookahead.peek(keyword::genesis_build) {
704			Ok(PalletAttr::GenesisBuild(content.parse::<keyword::genesis_build>()?.span()))
705		} else if lookahead.peek(keyword::validate_unsigned) {
706			Ok(PalletAttr::ValidateUnsigned(content.parse::<keyword::validate_unsigned>()?.span()))
707		} else if lookahead.peek(keyword::type_value) {
708			Ok(PalletAttr::TypeValue(content.parse::<keyword::type_value>()?.span()))
709		} else if lookahead.peek(keyword::extra_constants) {
710			Ok(PalletAttr::ExtraConstants(content.parse::<keyword::extra_constants>()?.span()))
711		} else if lookahead.peek(keyword::composite_enum) {
712			Ok(PalletAttr::Composite(content.parse::<keyword::composite_enum>()?.span()))
713		} else {
714			Err(lookahead.error())
715		}
716	}
717}
718
719/// The optional weight annotation on a `#[pallet::call]` like `#[pallet::call(weight($type))]`.
720#[derive(Clone)]
721pub struct InheritedCallWeightAttr {
722	pub typename: syn::Type,
723}
724
725impl syn::parse::Parse for InheritedCallWeightAttr {
726	// Parses `(weight($type))` or `(weight = $type)`.
727	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
728		let content;
729		syn::parenthesized!(content in input);
730		content.parse::<keyword::weight>()?;
731		let lookahead = content.lookahead1();
732
733		let buffer = if lookahead.peek(syn::token::Paren) {
734			let inner;
735			syn::parenthesized!(inner in content);
736			inner
737		} else if lookahead.peek(syn::Token![=]) {
738			content.parse::<syn::Token![=]>().expect("peeked");
739			content
740		} else {
741			return Err(lookahead.error());
742		};
743
744		Ok(Self { typename: buffer.parse()? })
745	}
746}