frame_support_procedural/pallet/parse/
config.rs1use super::helper;
19use frame_support_procedural_tools::{get_doc_literals, is_using_frame_crate};
20use quote::ToTokens;
21use syn::{spanned::Spanned, token, Token};
22
23mod keyword {
25 syn::custom_keyword!(Config);
26 syn::custom_keyword!(From);
27 syn::custom_keyword!(T);
28 syn::custom_keyword!(I);
29 syn::custom_keyword!(config);
30 syn::custom_keyword!(pallet);
31 syn::custom_keyword!(IsType);
32 syn::custom_keyword!(RuntimeEvent);
33 syn::custom_keyword!(Event);
34 syn::custom_keyword!(frame_system);
35 syn::custom_keyword!(disable_frame_system_supertrait_check);
36 syn::custom_keyword!(no_default);
37 syn::custom_keyword!(no_default_bounds);
38 syn::custom_keyword!(constant);
39}
40
41#[derive(Default)]
42pub struct DefaultTrait {
43 pub items: Vec<(syn::TraitItem, bool)>,
47 pub has_system: bool,
48}
49
50pub struct ConfigDef {
52 pub index: usize,
54 pub has_instance: bool,
56 pub consts_metadata: Vec<ConstMetadataDef>,
58 pub has_event_type: bool,
63 pub where_clause: Option<syn::WhereClause>,
65 pub default_sub_trait: Option<DefaultTrait>,
71}
72
73pub struct ConstMetadataDef {
75 pub ident: syn::Ident,
77 pub type_: syn::Type,
79 pub doc: Vec<syn::Expr>,
81 pub attrs: Vec<syn::Attribute>,
83}
84
85impl TryFrom<&syn::TraitItemType> for ConstMetadataDef {
86 type Error = syn::Error;
87
88 fn try_from(trait_ty: &syn::TraitItemType) -> Result<Self, Self::Error> {
89 let err = |span, msg| {
90 syn::Error::new(span, format!("Invalid usage of `#[pallet::constant]`: {}", msg))
91 };
92 let doc = get_doc_literals(&trait_ty.attrs);
93 let ident = trait_ty.ident.clone();
94 let bound = trait_ty
95 .bounds
96 .iter()
97 .find_map(|param_bound| {
98 let syn::TypeParamBound::Trait(trait_bound) = param_bound else { return None };
99
100 trait_bound.path.segments.last().and_then(|s| (s.ident == "Get").then(|| s))
101 })
102 .ok_or_else(|| err(trait_ty.span(), "`Get<T>` trait bound not found"))?;
103
104 let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments else {
105 return Err(err(bound.span(), "Expected trait generic args"));
106 };
107
108 if ab.args.len() != 1 {
110 return Err(err(bound.span(), "Expected a single type argument"));
111 }
112
113 let syn::GenericArgument::Type(ref type_arg) = ab.args[0] else {
114 return Err(err(ab.args[0].span(), "Expected a type argument"));
115 };
116
117 let type_ = syn::parse2::<syn::Type>(replace_self_by_t(type_arg.to_token_stream()))
118 .expect("Internal error: replacing `Self` by `T` should result in valid type");
119
120 Ok(Self { ident, type_, doc, attrs: trait_ty.attrs.clone() })
121 }
122}
123
124pub struct DisableFrameSystemSupertraitCheck;
126
127impl syn::parse::Parse for DisableFrameSystemSupertraitCheck {
128 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
129 input.parse::<syn::Token![#]>()?;
130 let content;
131 syn::bracketed!(content in input);
132 content.parse::<syn::Ident>()?;
133 content.parse::<syn::Token![::]>()?;
134
135 content.parse::<keyword::disable_frame_system_supertrait_check>()?;
136 Ok(Self)
137 }
138}
139
140#[derive(derive_syn_parse::Parse, PartialEq, Eq)]
142pub enum PalletAttrType {
143 #[peek(keyword::no_default, name = "no_default")]
144 NoDefault(keyword::no_default),
145 #[peek(keyword::no_default_bounds, name = "no_default_bounds")]
146 NoBounds(keyword::no_default_bounds),
147 #[peek(keyword::constant, name = "constant")]
148 Constant(keyword::constant),
149}
150
151#[derive(derive_syn_parse::Parse)]
153pub struct PalletAttr {
154 _pound: Token![#],
155 #[bracket]
156 _bracket: token::Bracket,
157 #[inside(_bracket)]
158 _pallet: keyword::pallet,
159 #[prefix(Token![::] in _bracket)]
160 #[inside(_bracket)]
161 typ: PalletAttrType,
162}
163
164pub struct IsTypeBoundEventParse(syn::Path);
166
167impl syn::parse::Parse for IsTypeBoundEventParse {
168 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
169 input.parse::<keyword::IsType>()?;
170 input.parse::<syn::Token![<]>()?;
171 input.parse::<syn::Token![<]>()?;
172 input.parse::<syn::Token![Self]>()?;
173 input.parse::<syn::Token![as]>()?;
174 let config_path = input.parse::<syn::Path>()?;
175 input.parse::<syn::Token![>]>()?;
176 input.parse::<syn::Token![::]>()?;
177 input.parse::<keyword::RuntimeEvent>()?;
178 input.parse::<syn::Token![>]>()?;
179
180 Ok(Self(config_path))
181 }
182}
183
184pub struct FromEventParse {
186 is_generic: bool,
187 has_instance: bool,
188}
189
190impl syn::parse::Parse for FromEventParse {
191 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
192 let mut is_generic = false;
193 let mut has_instance = false;
194
195 input.parse::<keyword::From>()?;
196 input.parse::<syn::Token![<]>()?;
197 input.parse::<keyword::Event>()?;
198 if input.peek(syn::Token![<]) {
199 is_generic = true;
200 input.parse::<syn::Token![<]>()?;
201 input.parse::<syn::Token![Self]>()?;
202 if input.peek(syn::Token![,]) {
203 input.parse::<syn::Token![,]>()?;
204 input.parse::<keyword::I>()?;
205 has_instance = true;
206 }
207 input.parse::<syn::Token![>]>()?;
208 }
209 input.parse::<syn::Token![>]>()?;
210
211 Ok(Self { is_generic, has_instance })
212 }
213}
214
215fn check_event_type(
218 frame_system: &syn::Path,
219 trait_item: &syn::TraitItem,
220 trait_has_instance: bool,
221) -> syn::Result<bool> {
222 let syn::TraitItem::Type(type_) = trait_item else { return Ok(false) };
223
224 if type_.ident != "RuntimeEvent" {
225 return Ok(false);
226 }
227
228 if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() {
230 let msg =
231 "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\
232 no generics nor where_clause";
233 return Err(syn::Error::new(trait_item.span(), msg));
234 }
235
236 let has_is_type_bound = type_.bounds.iter().any(|s| {
238 syn::parse2::<IsTypeBoundEventParse>(s.to_token_stream())
239 .map_or(false, |b| has_expected_system_config(b.0, frame_system))
240 });
241
242 if !has_is_type_bound {
243 let msg =
244 "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \
245 bound: `IsType<<Self as frame_system::Config>::RuntimeEvent>`"
246 .to_string();
247 return Err(syn::Error::new(type_.span(), msg));
248 }
249
250 let from_event_bound = type_
251 .bounds
252 .iter()
253 .find_map(|s| syn::parse2::<FromEventParse>(s.to_token_stream()).ok());
254
255 let Some(from_event_bound) = from_event_bound else {
256 let msg =
257 "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \
258 bound: `From<Event>` or `From<Event<Self>>` or `From<Event<Self, I>>`";
259 return Err(syn::Error::new(type_.span(), msg));
260 };
261
262 if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) {
263 let msg =
264 "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \
265 `From<Event..>`. Config and generic Event must be both with instance or \
266 without instance";
267 return Err(syn::Error::new(type_.span(), msg));
268 }
269
270 Ok(true)
271}
272
273fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool {
277 if path.segments.iter().all(|s| s.ident != "frame_system") {
279 return false;
280 }
281
282 let mut expected_system_config =
283 match (is_using_frame_crate(&path), is_using_frame_crate(&frame_system)) {
284 (true, false) =>
285 return false,
288 (false, true) =>
289 syn::parse2::<syn::Path>(quote::quote!(frame_system)).expect("is a valid path; qed"),
292 (_, _) =>
293 frame_system.clone(),
295 };
296
297 expected_system_config
298 .segments
299 .push(syn::PathSegment::from(syn::Ident::new("Config", path.span())));
300
301 expected_system_config
304 .segments
305 .into_iter()
306 .map(|ps| ps.ident)
307 .collect::<Vec<_>>() ==
308 path.segments.into_iter().map(|ps| ps.ident).collect::<Vec<_>>()
309}
310
311pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
313 input
314 .into_iter()
315 .map(|token_tree| match token_tree {
316 proc_macro2::TokenTree::Group(group) =>
317 proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into(),
318 proc_macro2::TokenTree::Ident(ident) if ident == "Self" =>
319 proc_macro2::Ident::new("T", ident.span()).into(),
320 other => other,
321 })
322 .collect()
323}
324
325impl ConfigDef {
326 pub fn try_from(
327 frame_system: &syn::Path,
328 index: usize,
329 item: &mut syn::Item,
330 enable_default: bool,
331 ) -> syn::Result<Self> {
332 let syn::Item::Trait(item) = item else {
333 let msg = "Invalid pallet::config, expected trait definition";
334 return Err(syn::Error::new(item.span(), msg));
335 };
336
337 if !matches!(item.vis, syn::Visibility::Public(_)) {
338 let msg = "Invalid pallet::config, trait must be public";
339 return Err(syn::Error::new(item.span(), msg));
340 }
341
342 syn::parse2::<keyword::Config>(item.ident.to_token_stream())?;
343
344 let where_clause = {
345 let stream = replace_self_by_t(item.generics.where_clause.to_token_stream());
346 syn::parse2::<Option<syn::WhereClause>>(stream).expect(
347 "Internal error: replacing `Self` by `T` should result in valid where
348 clause",
349 )
350 };
351
352 if item.generics.params.len() > 1 {
353 let msg = "Invalid pallet::config, expected no more than one generic";
354 return Err(syn::Error::new(item.generics.params[2].span(), msg));
355 }
356
357 let has_instance = if item.generics.params.first().is_some() {
358 helper::check_config_def_gen(&item.generics, item.ident.span())?;
359 true
360 } else {
361 false
362 };
363
364 let has_frame_system_supertrait = item.supertraits.iter().any(|s| {
365 syn::parse2::<syn::Path>(s.to_token_stream())
366 .map_or(false, |b| has_expected_system_config(b, frame_system))
367 });
368
369 let mut has_event_type = false;
370 let mut consts_metadata = vec![];
371 let mut default_sub_trait = if enable_default {
372 Some(DefaultTrait {
373 items: Default::default(),
374 has_system: has_frame_system_supertrait,
375 })
376 } else {
377 None
378 };
379 for trait_item in &mut item.items {
380 let is_event = check_event_type(frame_system, trait_item, has_instance)?;
381 has_event_type = has_event_type || is_event;
382
383 let mut already_no_default = false;
384 let mut already_constant = false;
385 let mut already_no_default_bounds = false;
386
387 while let Ok(Some(pallet_attr)) =
388 helper::take_first_item_pallet_attr::<PalletAttr>(trait_item)
389 {
390 match (pallet_attr.typ, &trait_item) {
391 (PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => {
392 if already_constant {
393 return Err(syn::Error::new(
394 pallet_attr._bracket.span.join(),
395 "Duplicate #[pallet::constant] attribute not allowed.",
396 ));
397 }
398 already_constant = true;
399 consts_metadata.push(ConstMetadataDef::try_from(typ)?);
400 },
401 (PalletAttrType::Constant(_), _) =>
402 return Err(syn::Error::new(
403 trait_item.span(),
404 "Invalid #[pallet::constant] in #[pallet::config], expected type item",
405 )),
406 (PalletAttrType::NoDefault(_), _) => {
407 if !enable_default {
408 return Err(syn::Error::new(
409 pallet_attr._bracket.span.join(),
410 "`#[pallet:no_default]` can only be used if `#[pallet::config(with_default)]` \
411 has been specified"
412 ));
413 }
414 if already_no_default {
415 return Err(syn::Error::new(
416 pallet_attr._bracket.span.join(),
417 "Duplicate #[pallet::no_default] attribute not allowed.",
418 ));
419 }
420
421 already_no_default = true;
422 },
423 (PalletAttrType::NoBounds(_), _) => {
424 if !enable_default {
425 return Err(syn::Error::new(
426 pallet_attr._bracket.span.join(),
427 "`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \
428 has been specified"
429 ));
430 }
431 if already_no_default_bounds {
432 return Err(syn::Error::new(
433 pallet_attr._bracket.span.join(),
434 "Duplicate #[pallet::no_default_bounds] attribute not allowed.",
435 ));
436 }
437 already_no_default_bounds = true;
438 },
439 }
440 }
441
442 if !already_no_default && enable_default {
443 default_sub_trait
444 .as_mut()
445 .expect("is 'Some(_)' if 'enable_default'; qed")
446 .items
447 .push((trait_item.clone(), already_no_default_bounds));
448 }
449 }
450
451 let attr: Option<DisableFrameSystemSupertraitCheck> =
452 helper::take_first_item_pallet_attr(&mut item.attrs)?;
453 let disable_system_supertrait_check = attr.is_some();
454
455 if !has_frame_system_supertrait && !disable_system_supertrait_check {
456 let found = if item.supertraits.is_empty() {
457 "none".to_string()
458 } else {
459 let mut found = item
460 .supertraits
461 .iter()
462 .fold(String::new(), |acc, s| format!("{}`{}`, ", acc, quote::quote!(#s)));
463 found.pop();
464 found.pop();
465 found
466 };
467
468 let msg = format!(
469 "Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \
470 found {}. \
471 (try `pub trait Config: frame_system::Config {{ ...` or \
472 `pub trait Config<I: 'static>: frame_system::Config {{ ...`). \
473 To disable this check, use `#[pallet::disable_frame_system_supertrait_check]`",
474 frame_system.to_token_stream(),
475 found,
476 );
477 return Err(syn::Error::new(item.span(), msg));
478 }
479
480 Ok(Self {
481 index,
482 has_instance,
483 consts_metadata,
484 has_event_type,
485 where_clause,
486 default_sub_trait,
487 })
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494 #[test]
495 fn has_expected_system_config_works() {
496 let frame_system = syn::parse2::<syn::Path>(quote::quote!(frame_system)).unwrap();
497 let path = syn::parse2::<syn::Path>(quote::quote!(frame_system::Config)).unwrap();
498 assert!(has_expected_system_config(path, &frame_system));
499 }
500
501 #[test]
502 fn has_expected_system_config_works_with_assoc_type() {
503 let frame_system = syn::parse2::<syn::Path>(quote::quote!(frame_system)).unwrap();
504 let path =
505 syn::parse2::<syn::Path>(quote::quote!(frame_system::Config<RuntimeCall = Call>))
506 .unwrap();
507 assert!(has_expected_system_config(path, &frame_system));
508 }
509
510 #[test]
511 fn has_expected_system_config_works_with_frame() {
512 let path = syn::parse2::<syn::Path>(quote::quote!(frame_system::Config)).unwrap();
513
514 let frame_system =
515 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system))
516 .unwrap();
517 assert!(has_expected_system_config(path.clone(), &frame_system));
518
519 let frame_system =
520 syn::parse2::<syn::Path>(quote::quote!(frame::deps::frame_system)).unwrap();
521 assert!(has_expected_system_config(path, &frame_system));
522 }
523
524 #[test]
525 fn has_expected_system_config_works_with_frame_full_path() {
526 let frame_system =
527 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system))
528 .unwrap();
529 let path =
530 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system::Config))
531 .unwrap();
532 assert!(has_expected_system_config(path, &frame_system));
533
534 let frame_system =
535 syn::parse2::<syn::Path>(quote::quote!(frame::deps::frame_system)).unwrap();
536 let path =
537 syn::parse2::<syn::Path>(quote::quote!(frame::deps::frame_system::Config)).unwrap();
538 assert!(has_expected_system_config(path, &frame_system));
539 }
540
541 #[test]
542 fn has_expected_system_config_works_with_other_frame_full_path() {
543 let frame_system =
544 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::xyz::frame_system)).unwrap();
545 let path =
546 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::xyz::frame_system::Config))
547 .unwrap();
548 assert!(has_expected_system_config(path, &frame_system));
549
550 let frame_system =
551 syn::parse2::<syn::Path>(quote::quote!(frame::xyz::frame_system)).unwrap();
552 let path =
553 syn::parse2::<syn::Path>(quote::quote!(frame::xyz::frame_system::Config)).unwrap();
554 assert!(has_expected_system_config(path, &frame_system));
555 }
556
557 #[test]
558 fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() {
559 let frame_system =
560 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::xyz::frame_system)).unwrap();
561 let path =
562 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system::Config))
563 .unwrap();
564 assert!(!has_expected_system_config(path, &frame_system));
565 }
566
567 #[test]
568 fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() {
569 let frame_system =
570 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system))
571 .unwrap();
572 let path =
573 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::xyz::frame_system::Config))
574 .unwrap();
575 assert!(!has_expected_system_config(path, &frame_system));
576 }
577
578 #[test]
579 fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() {
580 let frame_system = syn::parse2::<syn::Path>(quote::quote!(frame_system)).unwrap();
581 let path =
582 syn::parse2::<syn::Path>(quote::quote!(polkadot_sdk_frame::deps::frame_system::Config))
583 .unwrap();
584 assert!(!has_expected_system_config(path, &frame_system));
585 }
586
587 #[test]
588 fn has_expected_system_config_unexpected_frame_system() {
589 let frame_system =
590 syn::parse2::<syn::Path>(quote::quote!(framez::deps::frame_system)).unwrap();
591 let path = syn::parse2::<syn::Path>(quote::quote!(frame_system::Config)).unwrap();
592 assert!(!has_expected_system_config(path, &frame_system));
593 }
594
595 #[test]
596 fn has_expected_system_config_unexpected_path() {
597 let frame_system = syn::parse2::<syn::Path>(quote::quote!(frame_system)).unwrap();
598 let path = syn::parse2::<syn::Path>(quote::quote!(frame_system::ConfigSystem)).unwrap();
599 assert!(!has_expected_system_config(path, &frame_system));
600 }
601
602 #[test]
603 fn has_expected_system_config_not_frame_system() {
604 let frame_system = syn::parse2::<syn::Path>(quote::quote!(something)).unwrap();
605 let path = syn::parse2::<syn::Path>(quote::quote!(something::Config)).unwrap();
606 assert!(!has_expected_system_config(path, &frame_system));
607 }
608}