From 8d606dead7218ad8585d5e1b290b2730c7c278cb Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 08:38:14 +0100 Subject: [PATCH 01/10] Allow additional trait bounds for constants --- .../procedural/src/pallet/parse/config.rs | 57 ++++++++++++++----- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index 79d4680752b90..2de5e6a81ac87 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -16,6 +16,7 @@ // limitations under the License. use super::helper; +use core::convert::TryFrom; use syn::spanned::Spanned; use quote::ToTokens; @@ -25,7 +26,6 @@ mod keyword { syn::custom_keyword!(From); syn::custom_keyword!(T); syn::custom_keyword!(I); - syn::custom_keyword!(Get); syn::custom_keyword!(config); syn::custom_keyword!(IsType); syn::custom_keyword!(Event); @@ -62,19 +62,48 @@ pub struct ConstMetadataDef { pub doc: Vec, } -impl syn::parse::Parse for ConstMetadataDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let doc = helper::get_doc_literals(&syn::Attribute::parse_outer(input)?); - input.parse::()?; - let ident = input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - let mut type_ = input.parse::()?; - type_ = syn::parse2::(replace_self_by_t(type_.to_token_stream())) +impl TryFrom for ConstMetadataDef { + type Error = syn::Error; + + fn try_from(value: syn::TraitItemType) -> Result { + let doc = helper::get_doc_literals(&value.attrs); + let ident = value.ident.clone(); + let bound = value.bounds + .iter() + .find_map(|b| + if let syn::TypeParamBound::Trait(tb) = b { + tb.path.segments + .last() + .and_then(|s| if s.ident == "Get" { Some(s) } else { None } ) + } else { + None + } + ) + .ok_or_else(|| syn::Error::new( + value.bounds.span(), + "`Get` trait bound not found") + )?; + let type_arg = if let syn::PathArguments::AngleBracketed (ref ab) = bound.arguments { + if ab.args.len() == 1 { + if let syn::GenericArgument::Type(ref ty) = ab.args[0] { + Ok(ty) + } else { + Err(syn::Error::new( + ab.args[0].span(), + "Expected a type argument") + ) + } + } else { + Err(syn::Error::new( + ab.span(), + "Expected a single type argument") + ) + } + } else { + Err(syn::Error::new(bound.arguments.span(), "Expected trait angle bracketed args")) + }?; + let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) .expect("Internal error: replacing `Self` by `T` should result in valid type"); - input.parse::]>()?; - input.parse::()?; Ok(Self { ident, type_, doc }) } @@ -323,7 +352,7 @@ impl ConfigDef { if type_attrs_const.len() == 1 { match trait_item { syn::TraitItem::Type(type_) => { - let constant = syn::parse2::(type_.to_token_stream()) + let constant = ConstMetadataDef::try_from(type_.clone()) .map_err(|e| { let error_msg = "Invalid usage of `#[pallet::constant]`, syntax \ must be `type $SomeIdent: Get<$SomeType>;`"; From ec057736e35098d8a086aa4946eeb37e8deb4397 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 09:40:24 +0100 Subject: [PATCH 02/10] Add ui test for constants with additional trait bounds --- frame/support/test/tests/pallet_ui.rs | 1 + .../pass/trait_constant_valid_bounds.rs | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 frame/support/test/tests/pallet_ui/pass/trait_constant_valid_bounds.rs diff --git a/frame/support/test/tests/pallet_ui.rs b/frame/support/test/tests/pallet_ui.rs index 1836b06cabfdd..e5f4a54dfb000 100644 --- a/frame/support/test/tests/pallet_ui.rs +++ b/frame/support/test/tests/pallet_ui.rs @@ -23,4 +23,5 @@ fn pallet_ui() { let t = trybuild::TestCases::new(); t.compile_fail("tests/pallet_ui/*.rs"); + t.pass("tests/pallet_ui/pass/*.rs"); } diff --git a/frame/support/test/tests/pallet_ui/pass/trait_constant_valid_bounds.rs b/frame/support/test/tests/pallet_ui/pass/trait_constant_valid_bounds.rs new file mode 100644 index 0000000000000..889f44df5d22c --- /dev/null +++ b/frame/support/test/tests/pallet_ui/pass/trait_constant_valid_bounds.rs @@ -0,0 +1,26 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::BlockNumberFor; + + #[pallet::config] + pub trait Config: frame_system::Config { + #[pallet::constant] + type U: Get; + + #[pallet::constant] + type V: Get + From; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} +} + +fn main() { +} From 0715e7197ffd5c4d4dd70dcf579b55892497e2b8 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 09:48:20 +0100 Subject: [PATCH 03/10] Update trait constant ui test --- .../pallet_ui/trait_constant_invalid_bound.stderr | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr index 16c3531140eaa..323167b58b079 100644 --- a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr +++ b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr @@ -4,8 +4,10 @@ error: Invalid usage of `#[pallet::constant]`, syntax must be `type $SomeIdent: 9 | type U; | ^^^^ -error: expected `:` - --> $DIR/trait_constant_invalid_bound.rs:9:9 +error: `Get` trait bound not found + --> $DIR/trait_constant_invalid_bound.rs:1:1 | -9 | type U; - | ^ +1 | #[frame_support::pallet] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) From dd974eed4e9c5183f59a66e1bb45246cbb4b8bf3 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 10:15:36 +0100 Subject: [PATCH 04/10] Import syn::Error --- .../procedural/src/pallet/parse/config.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index 2de5e6a81ac87..6d169eaba29a3 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -17,7 +17,7 @@ use super::helper; use core::convert::TryFrom; -use syn::spanned::Spanned; +use syn::{Error, spanned::Spanned}; use quote::ToTokens; /// List of additional token to be used for parsing. @@ -79,28 +79,19 @@ impl TryFrom for ConstMetadataDef { None } ) - .ok_or_else(|| syn::Error::new( - value.bounds.span(), - "`Get` trait bound not found") - )?; + .ok_or_else(|| Error::new(value.bounds.span(), "`Get` trait bound not found"))?; let type_arg = if let syn::PathArguments::AngleBracketed (ref ab) = bound.arguments { if ab.args.len() == 1 { if let syn::GenericArgument::Type(ref ty) = ab.args[0] { Ok(ty) } else { - Err(syn::Error::new( - ab.args[0].span(), - "Expected a type argument") - ) + Err(Error::new(ab.args[0].span(), "Expected a type argument")) } } else { - Err(syn::Error::new( - ab.span(), - "Expected a single type argument") - ) + Err(Error::new(ab.span(), "Expected a single type argument")) } } else { - Err(syn::Error::new(bound.arguments.span(), "Expected trait angle bracketed args")) + Err(Error::new(bound.arguments.span(), "Expected trait angle bracketed args")) }?; let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) .expect("Internal error: replacing `Self` by `T` should result in valid type"); From dd129c9239ebbcb1d28300f70c44b3aaa04436bd Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 10:34:03 +0100 Subject: [PATCH 05/10] Use reference instead of cloning --- frame/support/procedural/src/pallet/parse/config.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index 6d169eaba29a3..ae447f8b4c4f7 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -62,10 +62,10 @@ pub struct ConstMetadataDef { pub doc: Vec, } -impl TryFrom for ConstMetadataDef { +impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { type Error = syn::Error; - fn try_from(value: syn::TraitItemType) -> Result { + fn try_from(value: &syn::TraitItemType) -> Result { let doc = helper::get_doc_literals(&value.attrs); let ident = value.ident.clone(); let bound = value.bounds @@ -342,8 +342,8 @@ impl ConfigDef { if type_attrs_const.len() == 1 { match trait_item { - syn::TraitItem::Type(type_) => { - let constant = ConstMetadataDef::try_from(type_.clone()) + syn::TraitItem::Type(ref type_) => { + let constant = ConstMetadataDef::try_from(type_) .map_err(|e| { let error_msg = "Invalid usage of `#[pallet::constant]`, syntax \ must be `type $SomeIdent: Get<$SomeType>;`"; From dfb8b1b0baa96ae4e034f3d5a254b32335a783cd Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 10:59:00 +0100 Subject: [PATCH 06/10] Add extra invalid bound ui test --- .../procedural/src/pallet/parse/config.rs | 14 +++++------ .../trait_constant_invalid_bound.stderr | 8 +++---- .../trait_constant_invalid_bound_lifetime.rs | 23 +++++++++++++++++++ ...ait_constant_invalid_bound_lifetime.stderr | 11 +++++++++ 4 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.rs create mode 100644 frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index ae447f8b4c4f7..a5af4b9abd860 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -65,10 +65,10 @@ pub struct ConstMetadataDef { impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { type Error = syn::Error; - fn try_from(value: &syn::TraitItemType) -> Result { - let doc = helper::get_doc_literals(&value.attrs); - let ident = value.ident.clone(); - let bound = value.bounds + fn try_from(trait_ty: &syn::TraitItemType) -> Result { + let doc = helper::get_doc_literals(&trait_ty.attrs); + let ident = trait_ty.ident.clone(); + let bound = trait_ty.bounds .iter() .find_map(|b| if let syn::TypeParamBound::Trait(tb) = b { @@ -79,7 +79,7 @@ impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { None } ) - .ok_or_else(|| Error::new(value.bounds.span(), "`Get` trait bound not found"))?; + .ok_or_else(|| Error::new(trait_ty.span(), "`Get` trait bound not found"))?; let type_arg = if let syn::PathArguments::AngleBracketed (ref ab) = bound.arguments { if ab.args.len() == 1 { if let syn::GenericArgument::Type(ref ty) = ab.args[0] { @@ -88,10 +88,10 @@ impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { Err(Error::new(ab.args[0].span(), "Expected a type argument")) } } else { - Err(Error::new(ab.span(), "Expected a single type argument")) + Err(Error::new(bound.span(), "Expected a single type argument")) } } else { - Err(Error::new(bound.arguments.span(), "Expected trait angle bracketed args")) + Err(Error::new(bound.span(), "Expected trait generic args")) }?; let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) .expect("Internal error: replacing `Self` by `T` should result in valid type"); diff --git a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr index 323167b58b079..726bf956ff0a2 100644 --- a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr +++ b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr @@ -5,9 +5,7 @@ error: Invalid usage of `#[pallet::constant]`, syntax must be `type $SomeIdent: | ^^^^ error: `Get` trait bound not found - --> $DIR/trait_constant_invalid_bound.rs:1:1 - | -1 | #[frame_support::pallet] - | ^^^^^^^^^^^^^^^^^^^^^^^^ + --> $DIR/trait_constant_invalid_bound.rs:9:3 | - = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) +9 | type U; + | ^^^^ diff --git a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.rs b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.rs new file mode 100644 index 0000000000000..47303f2b20a02 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.rs @@ -0,0 +1,23 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::Hooks; + use frame_system::pallet_prelude::BlockNumberFor; + + #[pallet::config] + pub trait Config: frame_system::Config { + #[pallet::constant] + type U: Get<'static>; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr new file mode 100644 index 0000000000000..537dd70ffbf98 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr @@ -0,0 +1,11 @@ +error: Invalid usage of `#[pallet::constant]`, syntax must be `type $SomeIdent: Get<$SomeType>;` + --> $DIR/trait_constant_invalid_bound2.rs:9:3 + | +9 | type U: Get<'static>; + | ^^^^ + +error: Expected a type argument + --> $DIR/trait_constant_invalid_bound2.rs:9:15 + | +9 | type U: Get<'static>; + | ^^^^^^^ From 110ab918ecb278613a78ca618ec22bece317d44a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 11:13:14 +0100 Subject: [PATCH 07/10] Out or order valid bounds --- .../test/tests/pallet_ui/pass/trait_constant_valid_bounds.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frame/support/test/tests/pallet_ui/pass/trait_constant_valid_bounds.rs b/frame/support/test/tests/pallet_ui/pass/trait_constant_valid_bounds.rs index 889f44df5d22c..71eb4f2992b39 100644 --- a/frame/support/test/tests/pallet_ui/pass/trait_constant_valid_bounds.rs +++ b/frame/support/test/tests/pallet_ui/pass/trait_constant_valid_bounds.rs @@ -10,6 +10,9 @@ mod pallet { #[pallet::constant] type V: Get + From; + + #[pallet::constant] + type W: From + Get; } #[pallet::pallet] From 1ef9ce0cc9614041b5a687cd920a05ca4a953efa Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 11:49:01 +0100 Subject: [PATCH 08/10] Fix ui test --- .../pallet_ui/trait_constant_invalid_bound_lifetime.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr index 537dd70ffbf98..fa7f42719b4c4 100644 --- a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr +++ b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr @@ -1,5 +1,5 @@ error: Invalid usage of `#[pallet::constant]`, syntax must be `type $SomeIdent: Get<$SomeType>;` - --> $DIR/trait_constant_invalid_bound2.rs:9:3 + --> $DIR/trait_constant_invalid_bound_lifetime.rs:9:3 | 9 | type U: Get<'static>; | ^^^^ From 8d46ec74c79dcaddc65cb4b6db3e6c0dd95bb0cf Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 11:49:20 +0100 Subject: [PATCH 09/10] Fix ui test --- .../pallet_ui/trait_constant_invalid_bound_lifetime.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr index fa7f42719b4c4..da8193c39d8f4 100644 --- a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr +++ b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr @@ -5,7 +5,7 @@ error: Invalid usage of `#[pallet::constant]`, syntax must be `type $SomeIdent: | ^^^^ error: Expected a type argument - --> $DIR/trait_constant_invalid_bound2.rs:9:15 + --> $DIR/trait_constant_invalid_bound_lifetime.rs:9:15 | 9 | type U: Get<'static>; | ^^^^^^^ From b1536e1ac6e9bcf3957ccb4a2fbf90fc95421a20 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 9 Jun 2021 16:38:13 +0100 Subject: [PATCH 10/10] Apply review suggestion about error message --- .../procedural/src/pallet/parse/config.rs | 22 +++++++------------ .../trait_constant_invalid_bound.stderr | 8 +------ ...ait_constant_invalid_bound_lifetime.stderr | 8 +------ 3 files changed, 10 insertions(+), 28 deletions(-) diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index a5af4b9abd860..69dfaeb7f9e9b 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -17,7 +17,7 @@ use super::helper; use core::convert::TryFrom; -use syn::{Error, spanned::Spanned}; +use syn::spanned::Spanned; use quote::ToTokens; /// List of additional token to be used for parsing. @@ -66,6 +66,8 @@ impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { type Error = syn::Error; fn try_from(trait_ty: &syn::TraitItemType) -> Result { + let err = |span, msg| + syn::Error::new(span, format!("Invalid usage of `#[pallet::constant]`: {}", msg)); let doc = helper::get_doc_literals(&trait_ty.attrs); let ident = trait_ty.ident.clone(); let bound = trait_ty.bounds @@ -79,19 +81,19 @@ impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { None } ) - .ok_or_else(|| Error::new(trait_ty.span(), "`Get` trait bound not found"))?; + .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; let type_arg = if let syn::PathArguments::AngleBracketed (ref ab) = bound.arguments { if ab.args.len() == 1 { if let syn::GenericArgument::Type(ref ty) = ab.args[0] { Ok(ty) } else { - Err(Error::new(ab.args[0].span(), "Expected a type argument")) + Err(err(ab.args[0].span(), "Expected a type argument")) } } else { - Err(Error::new(bound.span(), "Expected a single type argument")) + Err(err(bound.span(), "Expected a single type argument")) } } else { - Err(Error::new(bound.span(), "Expected trait generic args")) + Err(err(bound.span(), "Expected trait generic args")) }?; let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) .expect("Internal error: replacing `Self` by `T` should result in valid type"); @@ -343,15 +345,7 @@ impl ConfigDef { if type_attrs_const.len() == 1 { match trait_item { syn::TraitItem::Type(ref type_) => { - let constant = ConstMetadataDef::try_from(type_) - .map_err(|e| { - let error_msg = "Invalid usage of `#[pallet::constant]`, syntax \ - must be `type $SomeIdent: Get<$SomeType>;`"; - let mut err = syn::Error::new(type_.span(), error_msg); - err.combine(e); - err - })?; - + let constant = ConstMetadataDef::try_from(type_)?; consts_metadata.push(constant); }, _ => { diff --git a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr index 726bf956ff0a2..057ec6ffb2c75 100644 --- a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr +++ b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound.stderr @@ -1,10 +1,4 @@ -error: Invalid usage of `#[pallet::constant]`, syntax must be `type $SomeIdent: Get<$SomeType>;` - --> $DIR/trait_constant_invalid_bound.rs:9:3 - | -9 | type U; - | ^^^^ - -error: `Get` trait bound not found +error: Invalid usage of `#[pallet::constant]`: `Get` trait bound not found --> $DIR/trait_constant_invalid_bound.rs:9:3 | 9 | type U; diff --git a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr index da8193c39d8f4..8d830fed8f392 100644 --- a/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr +++ b/frame/support/test/tests/pallet_ui/trait_constant_invalid_bound_lifetime.stderr @@ -1,10 +1,4 @@ -error: Invalid usage of `#[pallet::constant]`, syntax must be `type $SomeIdent: Get<$SomeType>;` - --> $DIR/trait_constant_invalid_bound_lifetime.rs:9:3 - | -9 | type U: Get<'static>; - | ^^^^ - -error: Expected a type argument +error: Invalid usage of `#[pallet::constant]`: Expected a type argument --> $DIR/trait_constant_invalid_bound_lifetime.rs:9:15 | 9 | type U: Get<'static>;