1use 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
24mod 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 pub items: Vec<(syn::TraitItem, bool)>,
49 pub has_system: bool,
50}
51
52pub struct ConfigDef {
54 pub index: usize,
56 pub has_instance: bool,
58 pub consts_metadata: Vec<ConstMetadataDef>,
60 pub associated_types_metadata: Vec<AssociatedTypeMetadataDef>,
62 pub where_clause: Option<syn::WhereClause>,
64 pub default_sub_trait: Option<DefaultTrait>,
70 pub warnings: Vec<Warning>,
72}
73
74pub struct AssociatedTypeMetadataDef {
76 pub ident: syn::Ident,
78 pub doc: Vec<syn::Expr>,
80 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
94pub struct ConstMetadataDef {
96 pub ident: syn::Ident,
98 pub type_: syn::Type,
100 pub doc: Vec<syn::Expr>,
102 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 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
145pub 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#[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#[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
187pub 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
207pub 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
238fn 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 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 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
296fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool {
300 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 return false,
311 (false, true) =>
312 syn::parse2::<syn::Path>(quote::quote!(frame_system)).expect("is a valid path; qed"),
315 (_, _) =>
316 frame_system.clone(),
318 };
319
320 expected_system_config
321 .segments
322 .push(syn::PathSegment::from(syn::Ident::new("Config", path.span())));
323
324 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
334pub 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
348fn contains_type_info_bound(ty: &TraitItemType) -> bool {
350 const KNOWN_TYPE_INFO_BOUNDS: &[&str] = &[
351 "TypeInfo",
353 "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 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 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 (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 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 if !disable_associated_metadata && !is_event && !already_constant {
549 if let syn::TraitItem::Type(ref ty) = trait_item {
550 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}