@@ -318,83 +318,107 @@ fn flatten_associated_items_in_impl_fns(imp: &mut ItemImpl) -> Result<(), Error>
318318 } )
319319 . collect ( ) ;
320320
321- // Resolve Self::* in function input types and return types.
321+ // Resolve Self::* in function input types and return types, including
322+ // inside generic arguments like Vec<Self::Val>, Result<Self::Val, Error>, or &Self::Val.
323+ // Uses default 128 depth limit for types. This is a somewhat arbitrary limit if it needs
324+ // to be increased in the future.
322325 for item in imp. items . iter_mut ( ) {
323326 if let ImplItem :: Fn ( f) = item {
324327 for input in f. sig . inputs . iter_mut ( ) {
325328 if let FnArg :: Typed ( t) = input {
326- if let Some ( resolved) = resolve_self_type ( & t. ty , & associated_types) {
327- * t. ty = resolved;
328- }
329+ resolve_self_types ( & mut t. ty , & associated_types, 128 ) ?;
329330 }
330331 }
331332 if let ReturnType :: Type ( _, ty) = & mut f. sig . output {
332- if let Some ( resolved) = resolve_self_type ( ty, & associated_types) {
333- * * ty = resolved;
334- }
333+ resolve_self_types ( ty, & associated_types, 128 ) ?;
335334 }
336335 }
337336 }
338337
339- // Check for any remaining unresolved Self::* types in fn signatures.
340- for item in imp. items . iter ( ) {
341- if let ImplItem :: Fn ( f) = item {
342- for input in f. sig . inputs . iter ( ) {
343- if let FnArg :: Typed ( t) = input {
344- if let Some ( ident) = self_type_ident ( & t. ty ) {
345- return Err ( Error :: new (
346- t. ty . span ( ) ,
347- format ! (
348- "unresolved associated type `Self::{ident}` in function \
349- parameter; use a concrete type instead"
350- ) ,
351- ) ) ;
338+ Ok ( ( ) )
339+ }
340+
341+ /// Recursively resolve `Self::Ident` types within a type, including inside
342+ /// generic arguments like `Vec<Self::Val>`, `Result<Self::Val, Error>`, or `&Self::Val`.
343+ ///
344+ /// ### Errors
345+ /// If we cannot resolve the type or any unresolved `Self::Ident` remains after resolution.
346+ fn resolve_self_types (
347+ ty : & mut Type ,
348+ associated_types : & HashMap < Ident , Type > ,
349+ depth : usize ,
350+ ) -> Result < ( ) , Error > {
351+ if depth == 0 {
352+ return Err ( Error :: new (
353+ ty. span ( ) ,
354+ "unable to resolve type; type depth limit exceeded" ,
355+ ) ) ;
356+ }
357+
358+ if let Some ( ident) = self_type_ident ( ty) ? {
359+ if let Some ( resolved) = associated_types. get ( ident) . cloned ( ) {
360+ * ty = resolved;
361+ return resolve_self_types ( ty, associated_types, depth - 1 ) ;
362+ }
363+ return Err ( Error :: new (
364+ ty. span ( ) ,
365+ format ! ( "unresolved associated type `Self::{ident}`; use a concrete type instead" ) ,
366+ ) ) ;
367+ }
368+
369+ match ty {
370+ // Reject qualified Self paths like `<Self as Trait>::Foo`.
371+ Type :: Path ( TypePath { qself : Some ( qself) , .. } )
372+ if matches ! ( qself. ty. as_ref( ) , Type :: Path ( TypePath { qself: None , path } ) if path. is_ident( "Self" ) ) =>
373+ {
374+ Err ( Error :: new (
375+ ty. span ( ) ,
376+ "qualified associated types like `<Self as Trait>::Type` are not supported; use a concrete type instead" ,
377+ ) )
378+ }
379+ // Recurse into generic arguments of path types.
380+ Type :: Path ( TypePath { path, .. } ) => {
381+ for segment in path. segments . iter_mut ( ) {
382+ if let PathArguments :: AngleBracketed ( args) = & mut segment. arguments {
383+ for arg in args. args . iter_mut ( ) {
384+ if let GenericArgument :: Type ( inner_ty) = arg {
385+ resolve_self_types ( inner_ty, associated_types, depth - 1 ) ?;
386+ }
352387 }
353388 }
354389 }
355- if let ReturnType :: Type ( _, ty) = & f. sig . output {
356- if let Some ( ident) = self_type_ident ( ty) {
357- return Err ( Error :: new (
358- ty. span ( ) ,
359- format ! (
360- "unresolved associated type `Self::{ident}` in return type; \
361- use a concrete type instead"
362- ) ,
363- ) ) ;
364- }
365- }
390+ Ok ( ( ) )
366391 }
367- }
368-
369- Ok ( ( ) )
370- }
371-
372- /// If the type is `Self::Ident` and `Ident` exists in `associated_types`,
373- /// return the resolved type. Otherwise return `None`.
374- fn resolve_self_type ( ty : & Type , associated_types : & HashMap < Ident , Type > ) -> Option < Type > {
375- match self_type_ident ( ty) {
376- Some ( ident) => associated_types. get ( ident) . cloned ( ) ,
377- None => None ,
392+ // Recurse into reference types like &Self::Val.
393+ Type :: Reference ( TypeReference { elem, .. } ) => {
394+ resolve_self_types ( elem, associated_types, depth - 1 )
395+ }
396+ _ => Ok ( ( ) ) ,
378397 }
379398}
380399
381400/// If the type is `Self::Ident`, return the `Ident`. Otherwise return `None`.
382- fn self_type_ident ( ty : & Type ) -> Option < & Ident > {
401+ ///
402+ /// ### Errors
403+ /// If the type is a generic associated type like `Self::Foo<T>`.
404+ fn self_type_ident ( ty : & Type ) -> Result < Option < & Ident > , Error > {
383405 if let Type :: Path ( TypePath { qself : None , path } ) = ty {
384406 let segments = & path. segments ;
385407 if segments. len ( ) == 2
386408 && segments. first ( ) == Some ( & PathSegment :: from ( format_ident ! ( "Self" ) ) )
387409 {
388- if let Some ( PathSegment {
389- arguments : PathArguments :: None ,
390- ident,
391- } ) = segments. get ( 1 )
392- {
393- return Some ( ident) ;
410+ if let Some ( seg) = segments. get ( 1 ) {
411+ return match seg. arguments {
412+ PathArguments :: None => Ok ( Some ( & seg. ident ) ) ,
413+ _ => Err ( Error :: new (
414+ path. span ( ) ,
415+ format ! ( "generic associated types like `Self::{}<..>` are not supported; use a concrete type instead" , seg. ident) ,
416+ ) ) ,
417+ } ;
394418 }
395419 }
396420 }
397- None
421+ Ok ( None )
398422}
399423
400424pub fn ty_to_safe_ident_str ( ty : & Type ) -> String {
@@ -487,3 +511,180 @@ mod test_path_in_macro_rules {
487511 assert_paths_eq ( input, expected) ;
488512 }
489513}
514+
515+ #[ cfg( test) ]
516+ mod test_fns_parse {
517+ use super :: * ;
518+ use quote:: quote;
519+ use syn:: parse2;
520+
521+ /// Parse an impl block through HasFnsItem and return the resolved fns.
522+ fn parse_fns ( input : TokenStream ) -> syn:: Result < Vec < Fn > > {
523+ parse2 :: < HasFnsItem > ( input) . map ( |item| item. fns ( ) )
524+ }
525+
526+ /// Parse an impl block and return the string representation of the nth
527+ /// fn's input types (excluding self) and return type.
528+ fn parsed_fn_sig ( input : TokenStream , n : usize ) -> ( Vec < String > , String ) {
529+ let fns = parse_fns ( input) . expect ( "parse failed" ) ;
530+ let f = & fns[ n] ;
531+ let inputs: Vec < String > = f
532+ . inputs
533+ . iter ( )
534+ . filter_map ( |arg| match arg {
535+ FnArg :: Typed ( t) => Some ( quote ! ( #t) . to_string ( ) ) ,
536+ _ => None ,
537+ } )
538+ . collect ( ) ;
539+ let output = match & f. output {
540+ ReturnType :: Default => "()" . to_string ( ) ,
541+ ReturnType :: Type ( _, ty) => quote ! ( #ty) . to_string ( ) ,
542+ } ;
543+ ( inputs, output)
544+ }
545+
546+ #[ test]
547+ fn test_no_associated_types ( ) {
548+ let input = quote ! {
549+ impl MyContract {
550+ pub fn hello( x: u32 ) -> u64 { }
551+ }
552+ } ;
553+ let ( inputs, output) = parsed_fn_sig ( input, 0 ) ;
554+ assert_eq ! ( inputs, vec![ "x : u32" ] ) ;
555+ assert_eq ! ( output, "u64" ) ;
556+ }
557+
558+ #[ test]
559+ fn test_basic_param_and_return ( ) {
560+ let input = quote ! {
561+ impl MyContract {
562+ type Val = u64 ;
563+ pub fn get( x: Self :: Val ) -> Self :: Val { }
564+ }
565+ } ;
566+ let ( inputs, output) = parsed_fn_sig ( input, 0 ) ;
567+ assert_eq ! ( inputs, vec![ "x : u64" ] ) ;
568+ assert_eq ! ( output, "u64" ) ;
569+ }
570+
571+ #[ test]
572+ fn test_chained_two_step ( ) {
573+ let input = quote ! {
574+ impl MyContract {
575+ type A = u32 ;
576+ type B = Self :: A ;
577+ pub fn get( x: Self :: B ) { }
578+ }
579+ } ;
580+ let ( inputs, _) = parsed_fn_sig ( input, 0 ) ;
581+ assert_eq ! ( inputs, vec![ "x : u32" ] ) ;
582+ }
583+
584+ #[ test]
585+ fn test_wrapped_option ( ) {
586+ let input = quote ! {
587+ impl MyContract {
588+ type A = u64 ;
589+ pub fn get( x: Option <Self :: A >) { }
590+ }
591+ } ;
592+ let ( inputs, _) = parsed_fn_sig ( input, 0 ) ;
593+ assert_eq ! ( inputs, vec![ "x : Option < u64 >" ] ) ;
594+ }
595+
596+ #[ test]
597+ fn test_double_wrapped_result_vec ( ) {
598+ let input = quote ! {
599+ impl MyContract {
600+ type A = u64 ;
601+ pub fn get( x: Result <Vec <Self :: A >, Error >) { }
602+ }
603+ } ;
604+ let ( inputs, _) = parsed_fn_sig ( input, 0 ) ;
605+ assert_eq ! ( inputs, vec![ "x : Result < Vec < u64 > , Error >" ] ) ;
606+ }
607+
608+ #[ test]
609+ fn test_reject_qualified_self_path ( ) {
610+ let input = quote ! {
611+ impl MyContract {
612+ pub fn get( x: <Self as Trait >:: A ) { }
613+ }
614+ } ;
615+ let Err ( err) = parse_fns ( input) else {
616+ panic ! ( "expected error" ) ;
617+ } ;
618+ assert ! (
619+ err. to_string( ) . contains( "qualified associated types" ) ,
620+ "unexpected error: {err}"
621+ ) ;
622+ }
623+
624+ #[ test]
625+ fn test_reject_generic_associated_type ( ) {
626+ let input = quote ! {
627+ impl MyContract {
628+ pub fn get( x: Self :: Foo <u32 >) { }
629+ }
630+ } ;
631+ let Err ( err) = parse_fns ( input) else {
632+ panic ! ( "expected error" ) ;
633+ } ;
634+ assert ! (
635+ err. to_string( ) . contains( "generic associated types" ) ,
636+ "unexpected error: {err}"
637+ ) ;
638+ }
639+
640+ #[ test]
641+ fn test_reject_buried_qualified_self_path ( ) {
642+ let input = quote ! {
643+ impl MyContract {
644+ pub fn get( x: Result <Vec <<Self as Trait >:: A >, Error >) { }
645+ }
646+ } ;
647+ let Err ( err) = parse_fns ( input) else {
648+ panic ! ( "expected error" ) ;
649+ } ;
650+ assert ! (
651+ err. to_string( ) . contains( "qualified associated types" ) ,
652+ "unexpected error: {err}"
653+ ) ;
654+ }
655+
656+ #[ test]
657+ fn test_reject_unresolved_type ( ) {
658+ let input = quote ! {
659+ impl MyContract {
660+ pub fn get( x: Self :: Elsewhere ) { }
661+ }
662+ } ;
663+ let Err ( err) = parse_fns ( input) else {
664+ panic ! ( "expected error" ) ;
665+ } ;
666+ assert ! (
667+ err. to_string( )
668+ . contains( "unresolved associated type `Self::Elsewhere`" ) ,
669+ "unexpected error: {err}"
670+ ) ;
671+ }
672+
673+ #[ test]
674+ fn test_reject_recursive_cycle ( ) {
675+ let input = quote ! {
676+ impl MyContract {
677+ type A = Self :: B ;
678+ type B = Self :: A ;
679+ pub fn get( x: Self :: A ) { }
680+ }
681+ } ;
682+ let Err ( err) = parse_fns ( input) else {
683+ panic ! ( "expected error" ) ;
684+ } ;
685+ assert ! (
686+ err. to_string( ) . contains( "depth limit exceeded" ) ,
687+ "unexpected error: {err}"
688+ ) ;
689+ }
690+ }
0 commit comments