referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/pallet/parse/
config.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
18use super::helper;
19use frame_support_procedural_tools::{get_cfg_attributes, get_doc_literals, is_using_frame_crate};
20use proc_macro_warning::Warning;
21use quote::ToTokens;
22use syn::{parse_quote, spanned::Spanned, token, Token, TraitItemType};
23
24/// List of additional token to be used for parsing.
25mod keyword {
26	syn::custom_keyword!(Config);
27	syn::custom_keyword!(From);
28	syn::custom_keyword!(T);
29	syn::custom_keyword!(I);
30	syn::custom_keyword!(config);
31	syn::custom_keyword!(pallet);
32	syn::custom_keyword!(IsType);
33	syn::custom_keyword!(RuntimeEvent);
34	syn::custom_keyword!(Event);
35	syn::custom_keyword!(frame_system);
36	syn::custom_keyword!(disable_frame_system_supertrait_check);
37	syn::custom_keyword!(no_default);
38	syn::custom_keyword!(no_default_bounds);
39	syn::custom_keyword!(constant);
40	syn::custom_keyword!(include_metadata);
41}
42
43#[derive(Default)]
44pub struct DefaultTrait {
45	/// A bool for each sub-trait item indicates whether the item has
46	/// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds
47	/// in the generated default sub-trait.
48	pub items: Vec<(syn::TraitItem, bool)>,
49	pub has_system: bool,
50}
51
52/// Input definition for the pallet config.
53pub struct ConfigDef {
54	/// The index of item in pallet module.
55	pub index: usize,
56	/// Whether the trait has instance (i.e. define with `Config<I = ()>`)
57	pub has_instance: bool,
58	/// Const associated type.
59	pub consts_metadata: Vec<ConstMetadataDef>,
60	/// Associated types metadata.
61	pub associated_types_metadata: Vec<AssociatedTypeMetadataDef>,
62	/// The where clause on trait definition but modified so `Self` is `T`.
63	pub where_clause: Option<syn::WhereClause>,
64	/// Whether a default sub-trait should be generated.
65	///
66	/// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`).
67	/// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are
68	/// no trait items.
69	pub default_sub_trait: Option<DefaultTrait>,
70	/// Compile time warnings. Mainly for deprecated items.
71	pub warnings: Vec<Warning>,
72}
73
74/// Input definition for an associated type in pallet config.
75pub struct AssociatedTypeMetadataDef {
76	/// Name of the associated type.
77	pub ident: syn::Ident,
78	/// The doc associated.
79	pub doc: Vec<syn::Expr>,
80	/// The cfg associated.
81	pub cfg: Vec<syn::Attribute>,
82}
83
84impl From<&syn::TraitItemType> for AssociatedTypeMetadataDef {
85	fn from(trait_ty: &syn::TraitItemType) -> Self {
86		let ident = trait_ty.ident.clone();
87		let doc = get_doc_literals(&trait_ty.attrs);
88		let cfg = get_cfg_attributes(&trait_ty.attrs);
89
90		Self { ident, doc, cfg }
91	}
92}
93
94/// Input definition for a constant in pallet config.
95pub struct ConstMetadataDef {
96	/// Name of the associated type.
97	pub ident: syn::Ident,
98	/// The type in Get, e.g. `u32` in `type Foo: Get<u32>;`, but `Self` is replaced by `T`
99	pub type_: syn::Type,
100	/// The doc associated
101	pub doc: Vec<syn::Expr>,
102	/// attributes
103	pub attrs: Vec<syn::Attribute>,
104}
105
106impl TryFrom<&syn::TraitItemType> for ConstMetadataDef {
107	type Error = syn::Error;
108
109	fn try_from(trait_ty: &syn::TraitItemType) -> Result<Self, Self::Error> {
110		let err = |span, msg| {
111			syn::Error::new(span, format!("Invalid usage of `#[pallet::constant]`: {}", msg))
112		};
113		let doc = get_doc_literals(&trait_ty.attrs);
114		let ident = trait_ty.ident.clone();
115		let bound = trait_ty
116			.bounds
117			.iter()
118			.find_map(|param_bound| {
119				let syn::TypeParamBound::Trait(trait_bound) = param_bound else { return None };
120
121				trait_bound.path.segments.last().and_then(|s| (s.ident == "Get").then(|| s))
122			})
123			.ok_or_else(|| err(trait_ty.span(), "`Get<T>` trait bound not found"))?;
124
125		let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments else {
126			return Err(err(bound.span(), "Expected trait generic args"));
127		};
128
129		// Only one type argument is expected.
130		if ab.args.len() != 1 {
131			return Err(err(bound.span(), "Expected a single type argument"));
132		}
133
134		let syn::GenericArgument::Type(ref type_arg) = ab.args[0] else {
135			return Err(err(ab.args[0].span(), "Expected a type argument"));
136		};
137
138		let type_ = syn::parse2::<syn::Type>(replace_self_by_t(type_arg.to_token_stream()))
139			.expect("Internal error: replacing `Self` by `T` should result in valid type");
140
141		Ok(Self { ident, type_, doc, attrs: trait_ty.attrs.clone() })
142	}
143}
144
145/// Parse for `#[pallet::disable_frame_system_supertrait_check]`
146pub struct DisableFrameSystemSupertraitCheck;
147
148impl syn::parse::Parse for DisableFrameSystemSupertraitCheck {
149	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
150		input.parse::<syn::Token![#]>()?;
151		let content;
152		syn::bracketed!(content in input);
153		content.parse::<syn::Ident>()?;
154		content.parse::<syn::Token![::]>()?;
155
156		content.parse::<keyword::disable_frame_system_supertrait_check>()?;
157		Ok(Self)
158	}
159}
160
161/// Parsing for the `typ` portion of `PalletAttr`
162#[derive(derive_syn_parse::Parse, PartialEq, Eq)]
163pub enum PalletAttrType {
164	#[peek(keyword::no_default, name = "no_default")]
165	NoDefault(keyword::no_default),
166	#[peek(keyword::no_default_bounds, name = "no_default_bounds")]
167	NoBounds(keyword::no_default_bounds),
168	#[peek(keyword::constant, name = "constant")]
169	Constant(keyword::constant),
170	#[peek(keyword::include_metadata, name = "include_metadata")]
171	IncludeMetadata(keyword::include_metadata),
172}
173
174/// Parsing for `#[pallet::X]`
175#[derive(derive_syn_parse::Parse)]
176pub struct PalletAttr {
177	_pound: Token![#],
178	#[bracket]
179	_bracket: token::Bracket,
180	#[inside(_bracket)]
181	_pallet: keyword::pallet,
182	#[prefix(Token![::] in _bracket)]
183	#[inside(_bracket)]
184	typ: PalletAttrType,
185}
186
187/// Parse for `IsType<<Self as $path>::RuntimeEvent>` and retrieve `$path`
188pub struct IsTypeBoundEventParse(syn::Path);
189
190impl syn::parse::Parse for IsTypeBoundEventParse {
191	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
192		input.parse::<keyword::IsType>()?;
193		input.parse::<syn::Token![<]>()?;
194		input.parse::<syn::Token![<]>()?;
195		input.parse::<syn::Token![Self]>()?;
196		input.parse::<syn::Token![as]>()?;
197		let config_path = input.parse::<syn::Path>()?;
198		input.parse::<syn::Token![>]>()?;
199		input.parse::<syn::Token![::]>()?;
200		input.parse::<keyword::RuntimeEvent>()?;
201		input.parse::<syn::Token![>]>()?;
202
203		Ok(Self(config_path))
204	}
205}
206
207/// Parse for `From<Event>` or `From<Event<Self>>` or `From<Event<Self, I>>`
208pub struct FromEventParse {
209	is_generic: bool,
210	has_instance: bool,
211}
212
213impl syn::parse::Parse for FromEventParse {
214	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
215		let mut is_generic = false;
216		let mut has_instance = false;
217
218		input.parse::<keyword::From>()?;
219		input.parse::<syn::Token![<]>()?;
220		input.parse::<keyword::Event>()?;
221		if input.peek(syn::Token![<]) {
222			is_generic = true;
223			input.parse::<syn::Token![<]>()?;
224			input.parse::<syn::Token![Self]>()?;
225			if input.peek(syn::Token![,]) {
226				input.parse::<syn::Token![,]>()?;
227				input.parse::<keyword::I>()?;
228				has_instance = true;
229			}
230			input.parse::<syn::Token![>]>()?;
231		}
232		input.parse::<syn::Token![>]>()?;
233
234		Ok(Self { is_generic, has_instance })
235	}
236}
237
238/// Check if trait_item is `type RuntimeEvent`, if so checks its bounds are those expected.
239/// (Event type is reserved type)
240fn check_event_type(
241	frame_system: &syn::Path,
242	trait_item: &syn::TraitItem,
243	trait_has_instance: bool,
244) -> syn::Result<bool> {
245	let syn::TraitItem::Type(type_) = trait_item else { return Ok(false) };
246
247	if type_.ident != "RuntimeEvent" {
248		return Ok(false);
249	}
250
251	// Check event has no generics
252	if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() {
253		let msg =
254			"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\
255					no generics nor where_clause";
256		return Err(syn::Error::new(trait_item.span(), msg));
257	}
258
259	// Check bound contains IsType and From
260	let has_is_type_bound = type_.bounds.iter().any(|s| {
261		syn::parse2::<IsTypeBoundEventParse>(s.to_token_stream())
262			.map_or(false, |b| has_expected_system_config(b.0, frame_system))
263	});
264
265	if !has_is_type_bound {
266		let msg =
267			"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \
268					bound: `IsType<<Self as frame_system::Config>::RuntimeEvent>`"
269				.to_string();
270		return Err(syn::Error::new(type_.span(), msg));
271	}
272
273	let from_event_bound = type_
274		.bounds
275		.iter()
276		.find_map(|s| syn::parse2::<FromEventParse>(s.to_token_stream()).ok());
277
278	let Some(from_event_bound) = from_event_bound else {
279		let msg =
280			"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \
281				bound: `From<Event>` or `From<Event<Self>>` or `From<Event<Self, I>>`";
282		return Err(syn::Error::new(type_.span(), msg));
283	};
284
285	if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) {
286		let msg =
287			"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \
288					`From<Event..>`. Config and generic Event must be both with instance or \
289					without instance";
290		return Err(syn::Error::new(type_.span(), msg));
291	}
292
293	Ok(true)
294}
295
296/// Check that the path to `frame_system::Config` is valid, this is that the path is just
297/// `frame_system::Config` or when using the `frame` crate it is
298/// `polkadot_sdk_frame::xyz::frame_system::Config`.
299fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool {
300	// Check if `frame_system` is actually 'frame_system'.
301	if path.segments.iter().all(|s| s.ident != "frame_system") {
302		return false;
303	}
304
305	let mut expected_system_config =
306		match (is_using_frame_crate(&path), is_using_frame_crate(&frame_system)) {
307			(true, false) =>
308			// We can't use the path to `frame_system` from `frame` if `frame_system` is not being
309			// in scope through `frame`.
310				return false,
311			(false, true) =>
312			// We know that the only valid frame_system path is one that is `frame_system`, as
313			// `frame` re-exports it as such.
314				syn::parse2::<syn::Path>(quote::quote!(frame_system)).expect("is a valid path; qed"),
315			(_, _) =>
316			// They are either both `frame_system` or both `polkadot_sdk_frame::xyz::frame_system`.
317				frame_system.clone(),
318		};
319
320	expected_system_config
321		.segments
322		.push(syn::PathSegment::from(syn::Ident::new("Config", path.span())));
323
324	// the parse path might be something like `frame_system::Config<...>`, so we
325	// only compare the idents along the path.
326	expected_system_config
327		.segments
328		.into_iter()
329		.map(|ps| ps.ident)
330		.collect::<Vec<_>>() ==
331		path.segments.into_iter().map(|ps| ps.ident).collect::<Vec<_>>()
332}
333
334/// Replace ident `Self` by `T`
335pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
336	input
337		.into_iter()
338		.map(|token_tree| match token_tree {
339			proc_macro2::TokenTree::Group(group) =>
340				proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into(),
341			proc_macro2::TokenTree::Ident(ident) if ident == "Self" =>
342				proc_macro2::Ident::new("T", ident.span()).into(),
343			other => other,
344		})
345		.collect()
346}
347
348/// Check that the trait item requires the `TypeInfo` bound (or similar).
349fn contains_type_info_bound(ty: &TraitItemType) -> bool {
350	const KNOWN_TYPE_INFO_BOUNDS: &[&str] = &[
351		// Explicit TypeInfo trait.
352		"TypeInfo",
353		// Implicit known substrate traits that implement type info.
354		// Note: Aim to keep this list as small as possible.
355		"Parameter",
356	];
357
358	ty.bounds.iter().any(|bound| {
359		let syn::TypeParamBound::Trait(bound) = bound else { return false };
360
361		KNOWN_TYPE_INFO_BOUNDS
362			.iter()
363			.any(|known| bound.path.segments.last().map_or(false, |last| last.ident == *known))
364	})
365}
366
367impl ConfigDef {
368	pub fn try_from(
369		frame_system: &syn::Path,
370		index: usize,
371		item: &mut syn::Item,
372		enable_default: bool,
373		disable_associated_metadata: bool,
374		is_frame_system: bool,
375	) -> syn::Result<Self> {
376		let syn::Item::Trait(item) = item else {
377			let msg = "Invalid pallet::config, expected trait definition";
378			return Err(syn::Error::new(item.span(), msg));
379		};
380
381		if !matches!(item.vis, syn::Visibility::Public(_)) {
382			let msg = "Invalid pallet::config, trait must be public";
383			return Err(syn::Error::new(item.span(), msg));
384		}
385
386		syn::parse2::<keyword::Config>(item.ident.to_token_stream())?;
387
388		let where_clause = {
389			let stream = replace_self_by_t(item.generics.where_clause.to_token_stream());
390			syn::parse2::<Option<syn::WhereClause>>(stream).expect(
391				"Internal error: replacing `Self` by `T` should result in valid where
392					clause",
393			)
394		};
395
396		if item.generics.params.len() > 1 {
397			let msg = "Invalid pallet::config, expected no more than one generic";
398			return Err(syn::Error::new(item.generics.params[2].span(), msg));
399		}
400
401		let has_instance = if item.generics.params.first().is_some() {
402			helper::check_config_def_gen(&item.generics, item.ident.span())?;
403			true
404		} else {
405			false
406		};
407
408		let mut consts_metadata = vec![];
409		let mut associated_types_metadata = vec![];
410		let mut warnings = vec![];
411		let mut default_sub_trait = if enable_default {
412			Some(DefaultTrait { items: Default::default(), has_system: !is_frame_system })
413		} else {
414			None
415		};
416		for trait_item in &mut item.items {
417			let is_event = check_event_type(frame_system, trait_item, has_instance)?;
418
419			let mut already_no_default = false;
420			let mut already_constant = false;
421			let mut already_no_default_bounds = false;
422			let mut already_collected_associated_type = None;
423
424			// add deprecation notice for `RuntimeEvent`, iff pallet is not `frame_system`
425			if is_event && !is_frame_system {
426				if let syn::TraitItem::Type(type_event) = trait_item {
427					let allow_dep: syn::Attribute = parse_quote!(#[allow(deprecated)]);
428
429					// Check if the `#[allow(deprecated)]` attribute is present
430					if !type_event.attrs.iter().any(|attr| attr == &allow_dep) {
431						let warning = Warning::new_deprecated("RuntimeEvent")
432						.old("have `RuntimeEvent` associated type in the pallet config")
433						.new("remove it as it is redundant since associated bound gets appended automatically: \n
434							pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> { }")
435						.help_link("https://github.com/paritytech/polkadot-sdk/pull/7229")
436						.span(type_event.ident.span())
437						.build_or_panic();
438
439						warnings.push(warning);
440					}
441				}
442			}
443
444			while let Ok(Some(pallet_attr)) =
445				helper::take_first_item_pallet_attr::<PalletAttr>(trait_item)
446			{
447				match (pallet_attr.typ, &trait_item) {
448					(PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => {
449						if already_constant {
450							return Err(syn::Error::new(
451								pallet_attr._bracket.span.join(),
452								"Duplicate #[pallet::constant] attribute not allowed.",
453							));
454						}
455						already_constant = true;
456						consts_metadata.push(ConstMetadataDef::try_from(typ)?);
457					},
458					(PalletAttrType::Constant(_), _) =>
459						return Err(syn::Error::new(
460							trait_item.span(),
461							"Invalid #[pallet::constant] in #[pallet::config], expected type item",
462						)),
463					// Pallet developer has explicitly requested to include metadata for this associated type.
464					//
465					// They must provide a type item that implements `TypeInfo`.
466					(PalletAttrType::IncludeMetadata(_), syn::TraitItem::Type(ref typ)) => {
467						if already_collected_associated_type.is_some() {
468							return Err(syn::Error::new(
469								pallet_attr._bracket.span.join(),
470								"Duplicate #[pallet::include_metadata] attribute not allowed.",
471							));
472						}
473						already_collected_associated_type = Some(pallet_attr._bracket.span.join());
474						associated_types_metadata.push(AssociatedTypeMetadataDef::from(AssociatedTypeMetadataDef::from(typ)));
475					}
476					(PalletAttrType::IncludeMetadata(_), _) =>
477						return Err(syn::Error::new(
478							pallet_attr._bracket.span.join(),
479							"Invalid #[pallet::include_metadata] in #[pallet::config], expected type item",
480						)),
481					(PalletAttrType::NoDefault(_), _) => {
482						if !enable_default {
483							return Err(syn::Error::new(
484								pallet_attr._bracket.span.join(),
485								"`#[pallet::no_default]` can only be used if `#[pallet::config(with_default)]` \
486								has been specified"
487							));
488						}
489						if already_no_default {
490							return Err(syn::Error::new(
491								pallet_attr._bracket.span.join(),
492								"Duplicate #[pallet::no_default] attribute not allowed.",
493							));
494						}
495
496						already_no_default = true;
497					},
498					(PalletAttrType::NoBounds(_), _) => {
499						if !enable_default {
500							return Err(syn::Error::new(
501								pallet_attr._bracket.span.join(),
502								"`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \
503								has been specified"
504							));
505						}
506						if already_no_default_bounds {
507							return Err(syn::Error::new(
508								pallet_attr._bracket.span.join(),
509								"Duplicate #[pallet::no_default_bounds] attribute not allowed.",
510							));
511						}
512						already_no_default_bounds = true;
513					},
514				}
515			}
516
517			if let Some(span) = already_collected_associated_type {
518				// Events and constants are already propagated to the metadata
519				if is_event {
520					return Err(syn::Error::new(
521						span,
522						"Invalid #[pallet::include_metadata] for `type RuntimeEvent`. \
523						The associated type `RuntimeEvent` is already collected in the metadata.",
524					))
525				}
526
527				if already_constant {
528					return Err(syn::Error::new(
529						span,
530						"Invalid #[pallet::include_metadata]: conflict with #[pallet::constant]. \
531						Pallet constant already collect the metadata for the type.",
532					))
533				}
534
535				if let syn::TraitItem::Type(ref ty) = trait_item {
536					if !contains_type_info_bound(ty) {
537						let msg = format!(
538						"Invalid #[pallet::include_metadata] in #[pallet::config], collected type `{}` \
539						does not implement `TypeInfo` or `Parameter`",
540						ty.ident,
541					);
542						return Err(syn::Error::new(span, msg));
543					}
544				}
545			} else {
546				// Metadata of associated types is collected by default, if the associated type
547				// implements `TypeInfo`, or a similar trait that requires the `TypeInfo` bound.
548				if !disable_associated_metadata && !is_event && !already_constant {
549					if let syn::TraitItem::Type(ref ty) = trait_item {
550						// Collect the metadata of the associated type if it implements `TypeInfo`.
551						if contains_type_info_bound(ty) {
552							associated_types_metadata.push(AssociatedTypeMetadataDef::from(ty));
553						}
554					}
555				}
556			}
557
558			if !already_no_default && enable_default {
559				default_sub_trait
560					.as_mut()
561					.expect("is 'Some(_)' if 'enable_default'; qed")
562					.items
563					.push((trait_item.clone(), already_no_default_bounds));
564			}
565		}
566
567		let attr: Option<DisableFrameSystemSupertraitCheck> =
568			helper::take_first_item_pallet_attr(&mut item.attrs)?;
569		let disable_system_supertrait_check = attr.is_some();
570
571		let has_frame_system_supertrait = item.supertraits.iter().any(|s| {
572			syn::parse2::<syn::Path>(s.to_token_stream())
573				.map_or(false, |b| has_expected_system_config(b, frame_system))
574		});
575
576		if !has_frame_system_supertrait && !disable_system_supertrait_check {
577			let found = if item.supertraits.is_empty() {
578				"none".to_string()
579			} else {
580				let mut found = item
581					.supertraits
582					.iter()
583					.fold(String::new(), |acc, s| format!("{}`{}`, ", acc, quote::quote!(#s)));
584				found.pop();
585				found.pop();
586				found
587			};
588
589			let msg = format!(
590				"Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \
591				found {}. \
592				(try `pub trait Config: frame_system::Config {{ ...` or \
593				`pub trait Config<I: 'static>: frame_system::Config {{ ...`). \
594				To disable this check, use `#[pallet::disable_frame_system_supertrait_check]`",
595				frame_system.to_token_stream(),
596				found,
597			);
598			return Err(syn::Error::new(item.span(), msg));
599		}
600
601		Ok(Self {
602			index,
603			has_instance,
604			consts_metadata,
605			associated_types_metadata,
606			where_clause,
607			default_sub_trait,
608			warnings,
609		})
610	}
611}
612
613#[cfg(test)]
614mod tests {
615	use super::*;
616	#[test]
617	fn has_expected_system_config_works() {
618		let frame_system = syn::parse2::<syn::Path>(quote::quote!(frame_system)).unwrap();
619		let path = syn::parse2::<syn::Path>(quote::quote!(frame_system::Config)).unwrap();
620		assert!(has_expected_system_config(path, &frame_system));
621	}
622
623	#[test]
624	fn has_expected_system_config_works_with_assoc_type() {
625		let frame_system = syn::parse2::<syn::Path>(quote::quote!(frame_system)).unwrap();
626		let path =
627			syn::parse2::<syn::Path>(quote::quote!(frame_system::Config<RuntimeCall = Call>))
628				.unwrap();
629		assert!(has_expected_system_config(path, &frame_system));
630	}
631
632	#[test]
633	fn has_expected_system_config_works_with_frame() {
634		let path = syn::parse2::<syn::Path>(quote::quote!(frame_system::Config)).unwrap();
635
636		let frame_system =
637			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system))
638				.unwrap();
639		assert!(has_expected_system_config(path.clone(), &frame_system));
640
641		let frame_system =
642			syn::parse2::<syn::Path>(quote::quote!(frame::deps::frame_system)).unwrap();
643		assert!(has_expected_system_config(path, &frame_system));
644	}
645
646	#[test]
647	fn has_expected_system_config_works_with_frame_full_path() {
648		let frame_system =
649			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system))
650				.unwrap();
651		let path =
652			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system::Config))
653				.unwrap();
654		assert!(has_expected_system_config(path, &frame_system));
655
656		let frame_system =
657			syn::parse2::<syn::Path>(quote::quote!(frame::deps::frame_system)).unwrap();
658		let path =
659			syn::parse2::<syn::Path>(quote::quote!(frame::deps::frame_system::Config)).unwrap();
660		assert!(has_expected_system_config(path, &frame_system));
661	}
662
663	#[test]
664	fn has_expected_system_config_works_with_other_frame_full_path() {
665		let frame_system =
666			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::xyz::frame_system)).unwrap();
667		let path =
668			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::xyz::frame_system::Config))
669				.unwrap();
670		assert!(has_expected_system_config(path, &frame_system));
671
672		let frame_system =
673			syn::parse2::<syn::Path>(quote::quote!(frame::xyz::frame_system)).unwrap();
674		let path =
675			syn::parse2::<syn::Path>(quote::quote!(frame::xyz::frame_system::Config)).unwrap();
676		assert!(has_expected_system_config(path, &frame_system));
677	}
678
679	#[test]
680	fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() {
681		let frame_system =
682			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::xyz::frame_system)).unwrap();
683		let path =
684			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system::Config))
685				.unwrap();
686		assert!(!has_expected_system_config(path, &frame_system));
687	}
688
689	#[test]
690	fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() {
691		let frame_system =
692			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system))
693				.unwrap();
694		let path =
695			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::xyz::frame_system::Config))
696				.unwrap();
697		assert!(!has_expected_system_config(path, &frame_system));
698	}
699
700	#[test]
701	fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() {
702		let frame_system = syn::parse2::<syn::Path>(quote::quote!(frame_system)).unwrap();
703		let path =
704			syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system::Config))
705				.unwrap();
706		assert!(!has_expected_system_config(path, &frame_system));
707	}
708
709	#[test]
710	fn has_expected_system_config_unexpected_frame_system() {
711		let frame_system =
712			syn::parse2::<syn::Path>(quote::quote!(framez::deps::frame_system)).unwrap();
713		let path = syn::parse2::<syn::Path>(quote::quote!(frame_system::Config)).unwrap();
714		assert!(!has_expected_system_config(path, &frame_system));
715	}
716
717	#[test]
718	fn has_expected_system_config_unexpected_path() {
719		let frame_system = syn::parse2::<syn::Path>(quote::quote!(frame_system)).unwrap();
720		let path = syn::parse2::<syn::Path>(quote::quote!(frame_system::ConfigSystem)).unwrap();
721		assert!(!has_expected_system_config(path, &frame_system));
722	}
723
724	#[test]
725	fn has_expected_system_config_not_frame_system() {
726		let frame_system = syn::parse2::<syn::Path>(quote::quote!(something)).unwrap();
727		let path = syn::parse2::<syn::Path>(quote::quote!(something::Config)).unwrap();
728		assert!(!has_expected_system_config(path, &frame_system));
729	}
730}