diff --git a/Cargo.lock b/Cargo.lock index 42a51de70ec56..737863b433647 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1658,6 +1658,7 @@ dependencies = [ name = "frame-support-procedural" version = "2.0.0" dependencies = [ + "Inflector", "frame-support-procedural-tools", "proc-macro2", "quote", diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index 70662d710775d..1f1a2f93ccbf6 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -18,6 +18,7 @@ proc-macro = true frame-support-procedural-tools = { version = "2.0.0", path = "./tools" } proc-macro2 = "1.0.6" quote = "1.0.3" +Inflector = "0.11.4" syn = { version = "1.0.7", features = ["full"] } [features] diff --git a/frame/support/procedural/src/pallet/expand/call.rs b/frame/support/procedural/src/pallet/expand/call.rs index 2709995bf88b3..215997dfcf15e 100644 --- a/frame/support/procedural/src/pallet/expand/call.rs +++ b/frame/support/procedural/src/pallet/expand/call.rs @@ -24,9 +24,9 @@ use syn::spanned::Spanned; pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { let frame_support = &def.frame_support; let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(); - let type_decl_bounded_gen = &def.type_decl_bounded_generics(); - let type_use_gen = &def.type_use_generics(); + let type_impl_gen = &def.type_impl_generics(def.call.attr_span); + let type_decl_bounded_gen = &def.type_decl_bounded_generics(def.call.attr_span); + let type_use_gen = &def.type_use_generics(def.call.attr_span); let call_ident = syn::Ident::new("Call", def.call.attr_span.clone()); let pallet_ident = &def.pallet_struct.pallet; let where_clause = &def.call.where_clause; @@ -61,7 +61,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { method.args.iter() .map(|(is_compact, _, type_)| { let final_type = if *is_compact { - quote::quote!(Compact<#type_>) + quote::quote_spanned!(type_.span() => Compact<#type_>) } else { quote::quote!(#type_) }; diff --git a/frame/support/procedural/src/pallet/expand/constants.rs b/frame/support/procedural/src/pallet/expand/constants.rs index 5740d606a3325..e5acf42270aa7 100644 --- a/frame/support/procedural/src/pallet/expand/constants.rs +++ b/frame/support/procedural/src/pallet/expand/constants.rs @@ -33,9 +33,9 @@ struct ConstDef { /// * Impl fn module_constant_metadata for pallet. pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(); - let type_decl_gen = &def.type_decl_generics(); - let type_use_gen = &def.type_use_generics(); + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_decl_gen = &def.type_decl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); let pallet_ident = &def.pallet_struct.pallet; let mut where_clauses = vec![&def.config.where_clause]; diff --git a/frame/support/procedural/src/pallet/expand/error.rs b/frame/support/procedural/src/pallet/expand/error.rs index a88f626fdc525..c8c0a3c0c4d58 100644 --- a/frame/support/procedural/src/pallet/expand/error.rs +++ b/frame/support/procedural/src/pallet/expand/error.rs @@ -16,7 +16,6 @@ // limitations under the License. use crate::pallet::Def; -use syn::spanned::Spanned; /// * impl various trait on Error /// * impl ModuleErrorMetadata for Error @@ -27,13 +26,11 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { return Default::default() }; - let error_item_span = - def.item.content.as_mut().expect("Checked by def parser").1[error.index].span(); let error_ident = &error.error; let frame_support = &def.frame_support; let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(); - let type_use_gen = &def.type_use_generics(); + let type_impl_gen = &def.type_impl_generics(error.attr_span); + let type_use_gen = &def.type_use_generics(error.attr_span); let config_where_clause = &def.config.where_clause; let phantom_variant: syn::Variant = syn::parse_quote!( @@ -45,18 +42,20 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { ); let as_u8_matches = error.variants.iter().enumerate() - .map(|(i, (variant, _))| quote::quote!(Self::#variant => #i as u8,)); + .map(|(i, (variant, _))| { + quote::quote_spanned!(error.attr_span => Self::#variant => #i as u8,) + }); let as_str_matches = error.variants.iter() .map(|(variant, _)| { let variant_str = format!("{}", variant); - quote::quote!(Self::#variant => #variant_str,) + quote::quote_spanned!(error.attr_span => Self::#variant => #variant_str,) }); let metadata = error.variants.iter() .map(|(variant, doc)| { let variant_str = format!("{}", variant); - quote::quote!( + quote::quote_spanned!(error.attr_span => #frame_support::error::ErrorMetadata { name: #frame_support::error::DecodeDifferent::Encode(#variant_str), documentation: #frame_support::error::DecodeDifferent::Encode(&[ #( #doc, )* ]), @@ -69,13 +68,13 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { if let syn::Item::Enum(item) = item { item } else { - unreachable!("Checked by event parser") + unreachable!("Checked by error parser") } }; error_item.variants.insert(0, phantom_variant); - quote::quote_spanned!(error_item_span => + quote::quote_spanned!(error.attr_span => impl<#type_impl_gen> #frame_support::sp_std::fmt::Debug for #error_ident<#type_use_gen> #config_where_clause { diff --git a/frame/support/procedural/src/pallet/expand/event.rs b/frame/support/procedural/src/pallet/expand/event.rs index 76eda4448ba1a..e04d64750bca4 100644 --- a/frame/support/procedural/src/pallet/expand/event.rs +++ b/frame/support/procedural/src/pallet/expand/event.rs @@ -16,7 +16,6 @@ // limitations under the License. use crate::pallet::Def; -use syn::spanned::Spanned; /// * Add __Ignore variant on Event /// * Impl various trait on Event including metadata @@ -40,12 +39,12 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { let event_ident = &event.event; let frame_system = &def.frame_system; let frame_support = &def.frame_support; - let event_use_gen = &event.gen_kind.type_use_gen(); - let event_impl_gen= &event.gen_kind.type_impl_gen(); + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let event_impl_gen= &event.gen_kind.type_impl_gen(event.attr_span); let metadata = event.metadata.iter() .map(|(ident, args, docs)| { let name = format!("{}", ident); - quote::quote!( + quote::quote_spanned!(event.attr_span => #frame_support::event::EventMetadata { name: #frame_support::event::DecodeDifferent::Encode(#name), arguments: #frame_support::event::DecodeDifferent::Encode(&[ @@ -58,9 +57,6 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { ) }); - let event_item_span = - def.item.content.as_mut().expect("Checked by def parser").1[event.index].span(); - let event_item = { let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; if let syn::Item::Enum(item) = item { @@ -99,10 +95,10 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { let deposit_event = if let Some((fn_vis, fn_span)) = &event.deposit_event { - let event_use_gen = &event.gen_kind.type_use_gen(); - let trait_use_gen = &def.trait_use_generics(); - let type_impl_gen = &def.type_impl_generics(); - let type_use_gen = &def.type_use_generics(); + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let trait_use_gen = &def.trait_use_generics(event.attr_span); + let type_impl_gen = &def.type_impl_generics(event.attr_span); + let type_use_gen = &def.type_use_generics(event.attr_span); quote::quote_spanned!(*fn_span => impl<#type_impl_gen> Pallet<#type_use_gen> #completed_where_clause { @@ -125,7 +121,7 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { Default::default() }; - quote::quote_spanned!(event_item_span => + quote::quote_spanned!(event.attr_span => #deposit_event impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { diff --git a/frame/support/procedural/src/pallet/expand/genesis_build.rs b/frame/support/procedural/src/pallet/expand/genesis_build.rs index 8f42bfadc2004..374d21001d6a1 100644 --- a/frame/support/procedural/src/pallet/expand/genesis_build.rs +++ b/frame/support/procedural/src/pallet/expand/genesis_build.rs @@ -16,7 +16,6 @@ // limitations under the License. use crate::pallet::Def; -use syn::spanned::Spanned; /// * implement the trait `sp_runtime::BuildModuleGenesisStorage` /// * add #[cfg(features = "std")] to GenesisBuild implementation. @@ -26,21 +25,21 @@ pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream { } else { return Default::default() }; + let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser"); let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(); - let type_use_gen = &def.type_use_generics(); + let type_impl_gen = &def.type_impl_generics(genesis_build.attr_span); + let type_use_gen = &def.type_use_generics(genesis_build.attr_span); let trait_use_gen = if def.config.has_instance { - quote::quote!(T, I) + quote::quote_spanned!(genesis_build.attr_span => T, I) } else { // `__InherentHiddenInstance` used by construct_runtime here is alias for `()` - quote::quote!(T, ()) + quote::quote_spanned!(genesis_build.attr_span => T, ()) }; let gen_cfg_ident = &genesis_config.genesis_config; - let gen_cfg_use_gen = genesis_config.gen_kind.type_use_gen(); + let gen_cfg_use_gen = genesis_config.gen_kind.type_use_gen(genesis_build.attr_span); - let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser"); let genesis_build_item = &mut def.item.content.as_mut() .expect("Checked by def parser").1[genesis_build.index]; @@ -53,7 +52,7 @@ pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream { genesis_build_item_impl.attrs.push(syn::parse_quote!( #[cfg(feature = "std")] )); let where_clause = &genesis_build.where_clause; - quote::quote_spanned!(genesis_build_item.span() => + quote::quote_spanned!(genesis_build.attr_span => #[cfg(feature = "std")] impl<#type_impl_gen> #frame_support::sp_runtime::BuildModuleGenesisStorage<#trait_use_gen> for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index a20dac09166d2..2e4fddebb7b07 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -16,21 +16,17 @@ // limitations under the License. use crate::pallet::Def; -use syn::spanned::Spanned; /// * implement the individual traits using the Hooks trait pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(); - let type_use_gen = &def.type_use_generics(); + let type_impl_gen = &def.type_impl_generics(def.hooks.attr_span); + let type_use_gen = &def.type_use_generics(def.hooks.attr_span); let pallet_ident = &def.pallet_struct.pallet; let where_clause = &def.hooks.where_clause; let frame_system = &def.frame_system; - let hooks_item_span = def.item.content.as_mut() - .expect("Checked by def parser").1[def.hooks.index].span(); - - quote::quote_spanned!(hooks_item_span => + quote::quote_spanned!(def.hooks.attr_span => impl<#type_impl_gen> #frame_support::traits::OnFinalize<::BlockNumber> for #pallet_ident<#type_use_gen> #where_clause diff --git a/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/frame/support/procedural/src/pallet/expand/pallet_struct.rs index 6c89c0217ceca..aff7af4afb5e2 100644 --- a/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -25,9 +25,9 @@ use crate::pallet::Def; pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { let frame_support = &def.frame_support; let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(); - let type_use_gen = &def.type_use_generics(); - let type_decl_gen = &def.type_decl_generics(); + let type_impl_gen = &def.type_impl_generics(def.pallet_struct.attr_span); + let type_use_gen = &def.type_use_generics(def.pallet_struct.attr_span); + let type_decl_gen = &def.type_decl_generics(def.pallet_struct.attr_span); let pallet_ident = &def.pallet_struct.pallet; let config_where_clause = &def.config.where_clause; @@ -52,7 +52,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { let module_error_metadata = if let Some(error_def) = &def.error { let error_ident = &error_def.error; - quote::quote!( + quote::quote_spanned!(def.pallet_struct.attr_span => impl<#type_impl_gen> #frame_support::error::ModuleErrorMetadata for #pallet_ident<#type_use_gen> #config_where_clause @@ -65,7 +65,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { } ) } else { - quote::quote!( + quote::quote_spanned!(def.pallet_struct.attr_span => impl<#type_impl_gen> #frame_support::error::ModuleErrorMetadata for #pallet_ident<#type_use_gen> #config_where_clause @@ -77,7 +77,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { ) }; - quote::quote!( + quote::quote_spanned!(def.pallet_struct.attr_span => #module_error_metadata /// Type alias to `Pallet`, to be used by `construct_runtime`. diff --git a/frame/support/procedural/src/pallet/expand/storage.rs b/frame/support/procedural/src/pallet/expand/storage.rs index a77f9cf608499..7948fca2faf06 100644 --- a/frame/support/procedural/src/pallet/expand/storage.rs +++ b/frame/support/procedural/src/pallet/expand/storage.rs @@ -32,8 +32,6 @@ fn prefix_ident(storage_ident: &syn::Ident) -> syn::Ident { pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { let frame_support = &def.frame_support; let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(); - let type_use_gen = &def.type_use_generics(); let pallet_ident = &def.pallet_struct.pallet; // Replace first arg `_` by the generated prefix structure. @@ -63,6 +61,11 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { unreachable!("Checked by def"); }; + let type_use_gen = if def.config.has_instance { + quote::quote_spanned!(storage_def.attr_span => T, I) + } else { + quote::quote_spanned!(storage_def.attr_span => T) + }; let prefix_ident = prefix_ident(&storage_def.ident); args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); } @@ -72,22 +75,25 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { let docs = &storage.docs; let ident = &storage.ident; - let gen = &def.type_use_generics(); - let full_ident = quote::quote!( #ident<#gen> ); + let gen = &def.type_use_generics(storage.attr_span); + let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); let metadata_trait = match &storage.metadata { - Metadata::Value { .. } => - quote::quote!(#frame_support::storage::types::StorageValueMetadata), - Metadata::Map { .. } => - quote::quote!(#frame_support::storage::types::StorageMapMetadata), - Metadata::DoubleMap { .. } => - quote::quote!(#frame_support::storage::types::StorageDoubleMapMetadata), + Metadata::Value { .. } => quote::quote_spanned!(storage.attr_span => + #frame_support::storage::types::StorageValueMetadata + ), + Metadata::Map { .. } => quote::quote_spanned!(storage.attr_span => + #frame_support::storage::types::StorageMapMetadata + ), + Metadata::DoubleMap { .. } => quote::quote_spanned!(storage.attr_span => + #frame_support::storage::types::StorageDoubleMapMetadata + ), }; let ty = match &storage.metadata { Metadata::Value { value } => { let value = clean_type_string("e::quote!(#value).to_string()); - quote::quote!( + quote::quote_spanned!(storage.attr_span => #frame_support::metadata::StorageEntryType::Plain( #frame_support::metadata::DecodeDifferent::Encode(#value) ) @@ -96,7 +102,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { Metadata::Map { key, value } => { let value = clean_type_string("e::quote!(#value).to_string()); let key = clean_type_string("e::quote!(#key).to_string()); - quote::quote!( + quote::quote_spanned!(storage.attr_span => #frame_support::metadata::StorageEntryType::Map { hasher: <#full_ident as #metadata_trait>::HASHER, key: #frame_support::metadata::DecodeDifferent::Encode(#key), @@ -109,7 +115,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { let value = clean_type_string("e::quote!(#value).to_string()); let key1 = clean_type_string("e::quote!(#key1).to_string()); let key2 = clean_type_string("e::quote!(#key2).to_string()); - quote::quote!( + quote::quote_spanned!(storage.attr_span => #frame_support::metadata::StorageEntryType::DoubleMap { hasher: <#full_ident as #metadata_trait>::HASHER1, key2_hasher: <#full_ident as #metadata_trait>::HASHER2, @@ -121,7 +127,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { } }; - quote::quote_spanned!(storage.ident.span() => + quote::quote_spanned!(storage.attr_span => #frame_support::metadata::StorageEntryMetadata { name: #frame_support::metadata::DecodeDifferent::Encode( <#full_ident as #metadata_trait>::NAME @@ -144,19 +150,24 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { &storage.where_clause, &def.config.where_clause, ]); - let docs = storage.docs.iter().map(|d| quote::quote!(#[doc = #d])); + let docs = storage.docs.iter() + .map(|d| quote::quote_spanned!(storage.attr_span => #[doc = #d])); let ident = &storage.ident; - let gen = &def.type_use_generics(); - let full_ident = quote::quote!( #ident<#gen> ); + let gen = &def.type_use_generics(storage.attr_span); + let type_impl_gen = &def.type_impl_generics(storage.attr_span); + let type_use_gen = &def.type_use_generics(storage.attr_span); + let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); match &storage.metadata { Metadata::Value { value } => { let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote!(Option<#value>), + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), QueryKind::ValueQuery => quote::quote!(#value), }; - quote::quote_spanned!(getter.span() => + quote::quote_spanned!(storage.attr_span => impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { #( #docs )* pub fn #getter() -> #query { @@ -169,10 +180,12 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { }, Metadata::Map { key, value } => { let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote!(Option<#value>), + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), QueryKind::ValueQuery => quote::quote!(#value), }; - quote::quote_spanned!(getter.span() => + quote::quote_spanned!(storage.attr_span => impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { #( #docs )* pub fn #getter(k: KArg) -> #query where @@ -187,10 +200,12 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { }, Metadata::DoubleMap { key1, key2, value } => { let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote!(Option<#value>), + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), QueryKind::ValueQuery => quote::quote!(#value), }; - quote::quote_spanned!(getter.span() => + quote::quote_spanned!(storage.attr_span => impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { #( #docs )* pub fn #getter(k1: KArg1, k2: KArg2) -> #query where @@ -211,12 +226,14 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { }); let prefix_structs = def.storages.iter().map(|storage_def| { + let type_impl_gen = &def.type_impl_generics(storage_def.attr_span); + let type_use_gen = &def.type_use_generics(storage_def.attr_span); let prefix_struct_ident = prefix_ident(&storage_def.ident); let prefix_struct_vis = &storage_def.vis; let prefix_struct_const = storage_def.ident.to_string(); let config_where_clause = &def.config.where_clause; - quote::quote_spanned!(storage_def.ident.span() => + quote::quote_spanned!(storage_def.attr_span => #prefix_struct_vis struct #prefix_struct_ident<#type_use_gen>( core::marker::PhantomData<(#type_use_gen,)> ); @@ -239,6 +256,8 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { let mut where_clauses = vec![&def.config.where_clause]; where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); let completed_where_clause = super::merge_where_clauses(&where_clauses); + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); quote::quote!( impl<#type_impl_gen> #pallet_ident<#type_use_gen> diff --git a/frame/support/procedural/src/pallet/expand/store_trait.rs b/frame/support/procedural/src/pallet/expand/store_trait.rs index 1fa95addb18f4..cdc7e2837245f 100644 --- a/frame/support/procedural/src/pallet/expand/store_trait.rs +++ b/frame/support/procedural/src/pallet/expand/store_trait.rs @@ -28,8 +28,8 @@ pub fn expand_store_trait(def: &mut Def) -> proc_macro2::TokenStream { return Default::default() }; - let type_impl_gen = &def.type_impl_generics(); - let type_use_gen = &def.type_use_generics(); + let type_impl_gen = &def.type_impl_generics(trait_store.span()); + let type_use_gen = &def.type_use_generics(trait_store.span()); let pallet_ident = &def.pallet_struct.pallet; let mut where_clauses = vec![&def.config.where_clause]; diff --git a/frame/support/procedural/src/pallet/expand/type_value.rs b/frame/support/procedural/src/pallet/expand/type_value.rs index cb5d8307d89ed..b1b94eb4fbe6b 100644 --- a/frame/support/procedural/src/pallet/expand/type_value.rs +++ b/frame/support/procedural/src/pallet/expand/type_value.rs @@ -16,38 +16,56 @@ // limitations under the License. use crate::pallet::Def; -use syn::spanned::Spanned; /// * Generate the struct /// * implement the `Get<..>` on it +/// * Rename the name of the function to internal name pub fn expand_type_values(def: &mut Def) -> proc_macro2::TokenStream { let mut expand = quote::quote!(); let frame_support = &def.frame_support; for type_value in &def.type_values { - // Remove item from module content - let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index]; - let span = item.span(); - *item = syn::Item::Verbatim(Default::default()); + let fn_name_str = &type_value.ident.to_string(); + let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str); + let fn_ident_renamed = syn::Ident::new( + &format!("__type_value_for_{}", fn_name_snakecase), + type_value.ident.span(), + ); + + let type_value_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index]; + if let syn::Item::Fn(item) = item { + item + } else { + unreachable!("Checked by error parser") + } + }; + + // Rename the type_value function name + type_value_item.sig.ident = fn_ident_renamed.clone(); let vis = &type_value.vis; let ident = &type_value.ident; - let block = &type_value.block; let type_ = &type_value.type_; let where_clause = &type_value.where_clause; let (struct_impl_gen, struct_use_gen) = if type_value.is_generic { - (def.type_impl_generics(), def.type_use_generics()) + ( + def.type_impl_generics(type_value.attr_span), + def.type_use_generics(type_value.attr_span), + ) } else { (Default::default(), Default::default()) }; - expand.extend(quote::quote_spanned!(span => + expand.extend(quote::quote_spanned!(type_value.attr_span => #vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>); impl<#struct_impl_gen> #frame_support::traits::Get<#type_> for #ident<#struct_use_gen> #where_clause { - fn get() -> #type_ #block + fn get() -> #type_ { + #fn_ident_renamed::<#struct_use_gen>() + } } )); } diff --git a/frame/support/procedural/src/pallet/parse/call.rs b/frame/support/procedural/src/pallet/parse/call.rs index 92613fa981bbb..e26e2ca1ab5c4 100644 --- a/frame/support/procedural/src/pallet/parse/call.rs +++ b/frame/support/procedural/src/pallet/parse/call.rs @@ -40,7 +40,7 @@ pub struct CallDef { pub index: usize, /// Information on methods (used for expansion). pub methods: Vec, - /// The span of the attribute. + /// The span of the pallet::call attribute. pub attr_span: proc_macro2::Span, } @@ -124,7 +124,6 @@ pub fn check_dispatchable_first_arg_type(ty: &syn::Type) -> syn::Result<()> { impl CallDef { pub fn try_from( - // Span needed for expansion attr_span: proc_macro2::Span, index: usize, item: &mut syn::Item diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index 7684009bcb36f..44298c1d7fe44 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -48,7 +48,8 @@ pub struct ConfigDef { pub has_event_type: bool, /// The where clause on trait definition but modified so `Self` is `T`. pub where_clause: Option, - + /// The span of the pallet::config attribute. + pub attr_span: proc_macro2::Span, } /// Input definition for a constant in pallet config. @@ -262,8 +263,9 @@ pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenS impl ConfigDef { pub fn try_from( frame_system: &syn::Ident, + attr_span: proc_macro2::Span, index: usize, - item: &mut syn::Item + item: &mut syn::Item, ) -> syn::Result { let item = if let syn::Item::Trait(item) = item { item @@ -379,6 +381,7 @@ impl ConfigDef { consts_metadata, has_event_type, where_clause, + attr_span, }) } } diff --git a/frame/support/procedural/src/pallet/parse/error.rs b/frame/support/procedural/src/pallet/parse/error.rs index cc8b7f11ff405..49aaebc87f428 100644 --- a/frame/support/procedural/src/pallet/parse/error.rs +++ b/frame/support/procedural/src/pallet/parse/error.rs @@ -34,11 +34,17 @@ pub struct ErrorDef { /// A set of usage of instance, must be check for consistency with trait. pub instances: Vec, /// The keyword error used (contains span). - pub error: keyword::Error + pub error: keyword::Error, + /// The span of the pallet::error attribute. + pub attr_span: proc_macro2::Span, } impl ErrorDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { let item = if let syn::Item::Enum(item) = item { item } else { @@ -77,6 +83,7 @@ impl ErrorDef { .collect::>()?; Ok(ErrorDef { + attr_span, index, variants, instances, diff --git a/frame/support/procedural/src/pallet/parse/event.rs b/frame/support/procedural/src/pallet/parse/event.rs index ef0c3e2e92855..3d2f12a133b25 100644 --- a/frame/support/procedural/src/pallet/parse/event.rs +++ b/frame/support/procedural/src/pallet/parse/event.rs @@ -45,6 +45,8 @@ pub struct EventDef { pub deposit_event: Option<(syn::Visibility, proc_macro2::Span)>, /// Where clause used in event definition. pub where_clause: Option, + /// The span of the pallet::event attribute. + pub attr_span: proc_macro2::Span, } /// Attribute for Event: defines metadata name to use. @@ -150,7 +152,11 @@ impl PalletEventAttrInfo { } impl EventDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { let item = if let syn::Item::Enum(item) = item { item } else { @@ -208,6 +214,7 @@ impl EventDef { .collect(); Ok(EventDef { + attr_span, index, metadata, instances, diff --git a/frame/support/procedural/src/pallet/parse/genesis_build.rs b/frame/support/procedural/src/pallet/parse/genesis_build.rs index f9aa26d173a9a..1438c400b17f1 100644 --- a/frame/support/procedural/src/pallet/parse/genesis_build.rs +++ b/frame/support/procedural/src/pallet/parse/genesis_build.rs @@ -26,10 +26,16 @@ pub struct GenesisBuildDef { pub instances: Vec, /// The where_clause used. pub where_clause: Option, + /// The span of the pallet::genesis_build attribute. + pub attr_span: proc_macro2::Span, } impl GenesisBuildDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { let item = if let syn::Item::Impl(item) = item { item } else { @@ -48,6 +54,7 @@ impl GenesisBuildDef { instances.push(helper::check_genesis_builder_usage(&item_trait)?); Ok(Self { + attr_span, index, instances, where_clause: item.generics.where_clause.clone(), diff --git a/frame/support/procedural/src/pallet/parse/hooks.rs b/frame/support/procedural/src/pallet/parse/hooks.rs index f7fec5696d490..585222060e5f4 100644 --- a/frame/support/procedural/src/pallet/parse/hooks.rs +++ b/frame/support/procedural/src/pallet/parse/hooks.rs @@ -26,10 +26,16 @@ pub struct HooksDef { pub instances: Vec, /// The where_clause used. pub where_clause: Option, + /// The span of the pallet::hooks attribute. + pub attr_span: proc_macro2::Span, } impl HooksDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { let item = if let syn::Item::Impl(item) = item { item } else { @@ -61,6 +67,7 @@ impl HooksDef { } Ok(Self { + attr_span, index, instances, where_clause: item.generics.where_clause.clone(), diff --git a/frame/support/procedural/src/pallet/parse/mod.rs b/frame/support/procedural/src/pallet/parse/mod.rs index d7bb605a954a4..be54f709a47a5 100644 --- a/frame/support/procedural/src/pallet/parse/mod.rs +++ b/frame/support/procedural/src/pallet/parse/mod.rs @@ -92,38 +92,42 @@ impl Def { let pallet_attr: Option = helper::take_first_item_attr(item)?; match pallet_attr { - Some(PalletAttr::Config(_)) if config.is_none() => - config = Some(config::ConfigDef::try_from(&frame_system, index, item)?), - Some(PalletAttr::Pallet(_)) if pallet_struct.is_none() => - pallet_struct = Some(pallet_struct::PalletStructDef::try_from(index, item)?), - Some(PalletAttr::Hooks(_)) if hooks.is_none() => { - let m = hooks::HooksDef::try_from(index, item)?; + Some(PalletAttr::Config(span)) if config.is_none() => + config = Some(config::ConfigDef::try_from(&frame_system, span, index, item)?), + Some(PalletAttr::Pallet(span)) if pallet_struct.is_none() => { + let p = pallet_struct::PalletStructDef::try_from(span, index, item)?; + pallet_struct = Some(p); + }, + Some(PalletAttr::Hooks(span)) if hooks.is_none() => { + let m = hooks::HooksDef::try_from(span, index, item)?; hooks = Some(m); }, Some(PalletAttr::Call(span)) if call.is_none() => call = Some(call::CallDef::try_from(span, index, item)?), - Some(PalletAttr::Error(_)) if error.is_none() => - error = Some(error::ErrorDef::try_from(index, item)?), - Some(PalletAttr::Event(_)) if event.is_none() => - event = Some(event::EventDef::try_from(index, item)?), + Some(PalletAttr::Error(span)) if error.is_none() => + error = Some(error::ErrorDef::try_from(span, index, item)?), + Some(PalletAttr::Event(span)) if event.is_none() => + event = Some(event::EventDef::try_from(span, index, item)?), Some(PalletAttr::GenesisConfig(_)) if genesis_config.is_none() => { - genesis_config = - Some(genesis_config::GenesisConfigDef::try_from(index, item)?); + let g = genesis_config::GenesisConfigDef::try_from(index, item)?; + genesis_config = Some(g); + }, + Some(PalletAttr::GenesisBuild(span)) if genesis_build.is_none() => { + let g = genesis_build::GenesisBuildDef::try_from(span, index, item)?; + genesis_build = Some(g); }, - Some(PalletAttr::GenesisBuild(_)) if genesis_build.is_none() => - genesis_build = Some(genesis_build::GenesisBuildDef::try_from(index, item)?), Some(PalletAttr::Origin(_)) if origin.is_none() => origin = Some(origin::OriginDef::try_from(index, item)?), Some(PalletAttr::Inherent(_)) if inherent.is_none() => inherent = Some(inherent::InherentDef::try_from(index, item)?), - Some(PalletAttr::Storage(_)) => - storages.push(storage::StorageDef::try_from(index, item)?), + Some(PalletAttr::Storage(span)) => + storages.push(storage::StorageDef::try_from(span, index, item)?), Some(PalletAttr::ValidateUnsigned(_)) if validate_unsigned.is_none() => { let v = validate_unsigned::ValidateUnsignedDef::try_from(index, item)?; validate_unsigned = Some(v); }, - Some(PalletAttr::TypeValue(_)) => - type_values.push(type_value::TypeValueDef::try_from(index, item)?), + Some(PalletAttr::TypeValue(span)) => + type_values.push(type_value::TypeValueDef::try_from(span, index, item)?), Some(PalletAttr::ExtraConstants(_)) => { extra_constants = Some(extra_constants::ExtraConstantsDef::try_from(index, item)?) @@ -255,33 +259,33 @@ impl Def { /// Depending on if pallet is instantiable: /// * either `T: Config` /// * or `T: Config, I: 'static` - pub fn type_impl_generics(&self) -> proc_macro2::TokenStream { + pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { if self.config.has_instance { - quote::quote!(T: Config, I: 'static) + quote::quote_spanned!(span => T: Config, I: 'static) } else { - quote::quote!(T: Config) + quote::quote_spanned!(span => T: Config) } } /// Depending on if pallet is instantiable: /// * either `T: Config` /// * or `T: Config, I: 'static = ()` - pub fn type_decl_bounded_generics(&self) -> proc_macro2::TokenStream { + pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { if self.config.has_instance { - quote::quote!(T: Config, I: 'static = ()) + quote::quote_spanned!(span => T: Config, I: 'static = ()) } else { - quote::quote!(T: Config) + quote::quote_spanned!(span => T: Config) } } /// Depending on if pallet is instantiable: /// * either `T` /// * or `T, I = ()` - pub fn type_decl_generics(&self) -> proc_macro2::TokenStream { + pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { if self.config.has_instance { - quote::quote!(T, I = ()) + quote::quote_spanned!(span => T, I = ()) } else { - quote::quote!(T) + quote::quote_spanned!(span => T) } } @@ -289,22 +293,22 @@ impl Def { /// * either `` /// * or `` /// to be used when using pallet trait `Config` - pub fn trait_use_generics(&self) -> proc_macro2::TokenStream { + pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { if self.config.has_instance { - quote::quote!() + quote::quote_spanned!(span => ) } else { - quote::quote!() + quote::quote_spanned!(span => ) } } /// Depending on if pallet is instantiable: /// * either `T` /// * or `T, I` - pub fn type_use_generics(&self) -> proc_macro2::TokenStream { + pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { if self.config.has_instance { - quote::quote!(T, I) + quote::quote_spanned!(span => T, I) } else { - quote::quote!(T) + quote::quote_spanned!(span => T) } } } @@ -331,20 +335,20 @@ impl GenericKind { /// Return the generic to be used when using the type. /// /// Depending on its definition it can be: ``, `T` or `T, I` - pub fn type_use_gen(&self) -> proc_macro2::TokenStream { + pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { match self { GenericKind::None => quote::quote!(), - GenericKind::Config => quote::quote!(T), - GenericKind::ConfigAndInstance => quote::quote!(T, I), + GenericKind::Config => quote::quote_spanned!(span => T), + GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I), } } /// Return the generic to be used in `impl<..>` when implementing on the type. - pub fn type_impl_gen(&self) -> proc_macro2::TokenStream { + pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { match self { GenericKind::None => quote::quote!(), - GenericKind::Config => quote::quote!(T: Config), - GenericKind::ConfigAndInstance => quote::quote!(T: Config, I: 'static), + GenericKind::Config => quote::quote_spanned!(span => T: Config), + GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T: Config, I: 'static), } } diff --git a/frame/support/procedural/src/pallet/parse/pallet_struct.rs b/frame/support/procedural/src/pallet/parse/pallet_struct.rs index 8e7ddf27c4e73..1c979741d9803 100644 --- a/frame/support/procedural/src/pallet/parse/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/parse/pallet_struct.rs @@ -36,7 +36,9 @@ pub struct PalletStructDef { /// The keyword Pallet used (contains span). pub pallet: keyword::Pallet, /// Whether the trait `Store` must be generated. - pub store: Option<(syn::Visibility, keyword::Store)> + pub store: Option<(syn::Visibility, keyword::Store)>, + /// The span of the pallet::pallet attribute. + pub attr_span: proc_macro2::Span, } /// Parse for `#[pallet::generate_store($vis trait Store)]` @@ -64,7 +66,11 @@ impl syn::parse::Parse for PalletStructAttr { } impl PalletStructDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { let item = if let syn::Item::Struct(item) = item { item } else { @@ -94,6 +100,6 @@ impl PalletStructDef { let mut instances = vec![]; instances.push(helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?); - Ok(Self { index, instances, pallet, store }) + Ok(Self { index, instances, pallet, store, attr_span }) } } diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs index c744ad3b52e7d..cbf252a0c0738 100644 --- a/frame/support/procedural/src/pallet/parse/storage.rs +++ b/frame/support/procedural/src/pallet/parse/storage.rs @@ -89,6 +89,8 @@ pub struct StorageDef { pub query_kind: Option, /// Where clause of type definition. pub where_clause: Option, + /// The span of the pallet::storage attribute. + pub attr_span: proc_macro2::Span, } /// In `Foo` retrieve the argument at given position, i.e. A is argument at position 0. @@ -112,7 +114,11 @@ fn retrieve_arg( } impl StorageDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { let item = if let syn::Item::Type(item) = item { item } else { @@ -207,6 +213,7 @@ impl StorageDef { })?; Ok(StorageDef { + attr_span, index, vis: item.vis.clone(), ident: item.ident.clone(), diff --git a/frame/support/procedural/src/pallet/parse/type_value.rs b/frame/support/procedural/src/pallet/parse/type_value.rs index 7d675b82e7e94..5d901e772c915 100644 --- a/frame/support/procedural/src/pallet/parse/type_value.rs +++ b/frame/support/procedural/src/pallet/parse/type_value.rs @@ -36,10 +36,16 @@ pub struct TypeValueDef { pub instances: Vec, /// The where clause of the function. pub where_clause: Option, + /// The span of the pallet::type_value attribute. + pub attr_span: proc_macro2::Span, } impl TypeValueDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { let item = if let syn::Item::Fn(item) = item { item } else { @@ -88,6 +94,7 @@ impl TypeValueDef { let where_clause = item.sig.generics.where_clause.clone(); Ok(TypeValueDef { + attr_span, index, is_generic, vis, diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 911c060729a38..adea790a3fb03 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -1399,8 +1399,9 @@ pub mod pallet_prelude { /// /// ### Macro expansion /// -/// Macro generate struct with the name of the function and its generic, and implement -/// `Get<$ReturnType>` on it using the provided function block. +/// Macro renames the function to some internal name, generate a struct with the original name of +/// the function and its generic, and implement `Get<$ReturnType>` by calling the user defined +/// function. /// /// # Genesis config: `#[pallet::genesis_config]` optional /// diff --git a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr index 64f93cd574ed0..1eaf71be17104 100644 --- a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr +++ b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr @@ -6,8 +6,8 @@ error[E0369]: binary operation `==` cannot be applied to type `&::Bar: Clone` is not satisfied --> $DIR/call_argument_invalid_bound.rs:20:37 diff --git a/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr b/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr index f0f41a75deb46..f8ba5ecdc21b3 100644 --- a/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr +++ b/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr @@ -4,5 +4,5 @@ error[E0446]: private type `_GeneratedPrefixForStorageFoo` in public interfac 11 | #[pallet::generate_store(pub trait Store)] | ^^^^^ can't leak private type ... -21 | type Foo = StorageValue<_, u8>; - | - `_GeneratedPrefixForStorageFoo` declared as private +20 | #[pallet::storage] + | - `_GeneratedPrefixForStorageFoo` declared as private diff --git a/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.rs b/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.rs new file mode 100644 index 0000000000000..9c0662e3f77cb --- /dev/null +++ b/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.rs @@ -0,0 +1,28 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::{Hooks, PhantomData}; + use frame_system::pallet_prelude::BlockNumberFor; + + #[pallet::config] + pub trait Config: frame_system::Config + where ::AccountId: From + {} + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet + where ::AccountId: From + {} + + #[pallet::call] + impl Pallet + where ::AccountId: From + {} + + #[pallet::type_value] fn Foo() -> u32 { 3u32 } +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr b/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr new file mode 100644 index 0000000000000..85d7342b253d4 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/type_value_forgotten_where_clause.stderr @@ -0,0 +1,47 @@ +error[E0277]: the trait bound `::AccountId: From` is not satisfied + --> $DIR/type_value_forgotten_where_clause.rs:24:34 + | +7 | pub trait Config: frame_system::Config + | ------ required by a bound in this +8 | where ::AccountId: From + | --------- required by this bound in `pallet::Config` +... +24 | #[pallet::type_value] fn Foo() -> u32 { 3u32 } + | ^^^^^^ the trait `From` is not implemented for `::AccountId` + | +help: consider further restricting the associated type + | +24 | #[pallet::type_value] fn Foo() -> u32 where ::AccountId: From { 3u32 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `::AccountId: From` is not satisfied + --> $DIR/type_value_forgotten_where_clause.rs:24:12 + | +7 | pub trait Config: frame_system::Config + | ------ required by a bound in this +8 | where ::AccountId: From + | --------- required by this bound in `pallet::Config` +... +24 | #[pallet::type_value] fn Foo() -> u32 { 3u32 } + | ^^^^^^^^^^ the trait `From` is not implemented for `::AccountId` + | +help: consider further restricting the associated type + | +24 | #[pallet::type_value where ::AccountId: From] fn Foo() -> u32 { 3u32 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `::AccountId: From` is not satisfied + --> $DIR/type_value_forgotten_where_clause.rs:24:12 + | +7 | pub trait Config: frame_system::Config + | ------ required by a bound in this +8 | where ::AccountId: From + | --------- required by this bound in `pallet::Config` +... +24 | #[pallet::type_value] fn Foo() -> u32 { 3u32 } + | ^^^^^^^^^^ the trait `From` is not implemented for `::AccountId` + | +help: consider further restricting the associated type + | +24 | #[pallet::type_value] fn Foo() -> u32 where ::AccountId: From { 3u32 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^