33use std:: ops;
44
55pub ( crate ) use gen_trait_fn_body:: gen_trait_fn_body;
6- use hir:: { db:: HirDatabase , HasAttrs as HirHasAttrs , HirDisplay , InFile , Semantics } ;
6+ use hir:: { db:: HirDatabase , known , HasAttrs as HirHasAttrs , HirDisplay , InFile , Semantics } ;
77use ide_db:: {
88 famous_defs:: FamousDefs , path_transform:: PathTransform ,
99 syntax_helpers:: insert_whitespace_into_node:: insert_ws_into, RootDatabase , SnippetCap ,
@@ -600,6 +600,7 @@ pub(crate) fn add_method_to_adt(
600600pub ( crate ) struct ReferenceConversion {
601601 conversion : ReferenceConversionType ,
602602 ty : hir:: Type ,
603+ impls_deref : bool ,
603604}
604605
605606#[ derive( Debug ) ]
@@ -656,7 +657,13 @@ impl ReferenceConversion {
656657 | ReferenceConversionType :: AsRefSlice
657658 | ReferenceConversionType :: Dereferenced
658659 | ReferenceConversionType :: Option
659- | ReferenceConversionType :: Result => format ! ( "self.{field_name}.as_ref()" ) ,
660+ | ReferenceConversionType :: Result => {
661+ if self . impls_deref {
662+ format ! ( "&self.{field_name}" )
663+ } else {
664+ format ! ( "self.{field_name}.as_ref()" )
665+ }
666+ }
660667 }
661668 }
662669}
@@ -675,54 +682,88 @@ pub(crate) fn convert_reference_type(
675682 . or_else ( || handle_dereferenced ( & ty, db, famous_defs) )
676683 . or_else ( || handle_option_as_ref ( & ty, db, famous_defs) )
677684 . or_else ( || handle_result_as_ref ( & ty, db, famous_defs) )
678- . map ( |conversion| ReferenceConversion { ty, conversion } )
685+ . map ( |( conversion, impls_deref) | ReferenceConversion { ty, conversion, impls_deref } )
686+ }
687+
688+ fn impls_deref_for_target (
689+ ty : & hir:: Type ,
690+ target : hir:: Type ,
691+ db : & dyn HirDatabase ,
692+ famous_defs : & FamousDefs < ' _ , ' _ > ,
693+ ) -> bool {
694+ if let Some ( deref_trait) = famous_defs. core_ops_Deref ( ) {
695+ if ty. impls_trait ( db, deref_trait, & [ ] ) {
696+ let assoc_type_item = deref_trait. items ( db) . into_iter ( ) . find_map ( |item| match item {
697+ hir:: AssocItem :: TypeAlias ( alias) if alias. name ( db) == known:: Target => Some ( alias) ,
698+ _ => None ,
699+ } ) ;
700+ if let Some ( assoc_type_item) = assoc_type_item {
701+ matches ! (
702+ ty. normalize_trait_assoc_type( db, & [ ] , assoc_type_item) ,
703+ Some ( ty) if ty. could_coerce_to( db, & target) ,
704+ )
705+ } else {
706+ false
707+ }
708+ } else {
709+ false
710+ }
711+ } else {
712+ false
713+ }
679714}
680715
681- fn handle_copy ( ty : & hir:: Type , db : & dyn HirDatabase ) -> Option < ReferenceConversionType > {
682- ty. is_copy ( db) . then_some ( ReferenceConversionType :: Copy )
716+ fn handle_copy ( ty : & hir:: Type , db : & dyn HirDatabase ) -> Option < ( ReferenceConversionType , bool ) > {
717+ ty. is_copy ( db) . then_some ( ( ReferenceConversionType :: Copy , true ) )
683718}
684719
685720fn handle_as_ref_str (
686721 ty : & hir:: Type ,
687722 db : & dyn HirDatabase ,
688723 famous_defs : & FamousDefs < ' _ , ' _ > ,
689- ) -> Option < ReferenceConversionType > {
724+ ) -> Option < ( ReferenceConversionType , bool ) > {
690725 let str_type = hir:: BuiltinType :: str ( ) . ty ( db) ;
691726
692- ty. impls_trait ( db, famous_defs. core_convert_AsRef ( ) ?, & [ str_type] )
693- . then_some ( ReferenceConversionType :: AsRefStr )
727+ ty. impls_trait ( db, famous_defs. core_convert_AsRef ( ) ?, & [ str_type. clone ( ) ] ) . then_some ( (
728+ ReferenceConversionType :: AsRefStr ,
729+ impls_deref_for_target ( ty, str_type, db, famous_defs) ,
730+ ) )
694731}
695732
696733fn handle_as_ref_slice (
697734 ty : & hir:: Type ,
698735 db : & dyn HirDatabase ,
699736 famous_defs : & FamousDefs < ' _ , ' _ > ,
700- ) -> Option < ReferenceConversionType > {
737+ ) -> Option < ( ReferenceConversionType , bool ) > {
701738 let type_argument = ty. type_arguments ( ) . next ( ) ?;
702739 let slice_type = hir:: Type :: new_slice ( type_argument) ;
703740
704- ty. impls_trait ( db, famous_defs. core_convert_AsRef ( ) ?, & [ slice_type] )
705- . then_some ( ReferenceConversionType :: AsRefSlice )
741+ ty. impls_trait ( db, famous_defs. core_convert_AsRef ( ) ?, & [ slice_type. clone ( ) ] ) . then_some ( (
742+ ReferenceConversionType :: AsRefSlice ,
743+ impls_deref_for_target ( ty, slice_type, db, famous_defs) ,
744+ ) )
706745}
707746
708747fn handle_dereferenced (
709748 ty : & hir:: Type ,
710749 db : & dyn HirDatabase ,
711750 famous_defs : & FamousDefs < ' _ , ' _ > ,
712- ) -> Option < ReferenceConversionType > {
751+ ) -> Option < ( ReferenceConversionType , bool ) > {
713752 let type_argument = ty. type_arguments ( ) . next ( ) ?;
714753
715- ty. impls_trait ( db, famous_defs. core_convert_AsRef ( ) ?, & [ type_argument] )
716- . then_some ( ReferenceConversionType :: Dereferenced )
754+ ty. impls_trait ( db, famous_defs. core_convert_AsRef ( ) ?, & [ type_argument. clone ( ) ] ) . then_some ( (
755+ ReferenceConversionType :: Dereferenced ,
756+ impls_deref_for_target ( ty, type_argument, db, famous_defs) ,
757+ ) )
717758}
718759
719760fn handle_option_as_ref (
720761 ty : & hir:: Type ,
721762 db : & dyn HirDatabase ,
722763 famous_defs : & FamousDefs < ' _ , ' _ > ,
723- ) -> Option < ReferenceConversionType > {
764+ ) -> Option < ( ReferenceConversionType , bool ) > {
724765 if ty. as_adt ( ) == famous_defs. core_option_Option ( ) ?. ty ( db) . as_adt ( ) {
725- Some ( ReferenceConversionType :: Option )
766+ Some ( ( ReferenceConversionType :: Option , false ) )
726767 } else {
727768 None
728769 }
@@ -732,9 +773,9 @@ fn handle_result_as_ref(
732773 ty : & hir:: Type ,
733774 db : & dyn HirDatabase ,
734775 famous_defs : & FamousDefs < ' _ , ' _ > ,
735- ) -> Option < ReferenceConversionType > {
776+ ) -> Option < ( ReferenceConversionType , bool ) > {
736777 if ty. as_adt ( ) == famous_defs. core_result_Result ( ) ?. ty ( db) . as_adt ( ) {
737- Some ( ReferenceConversionType :: Result )
778+ Some ( ( ReferenceConversionType :: Result , false ) )
738779 } else {
739780 None
740781 }
0 commit comments