@@ -5,10 +5,11 @@ use crate::{
55 navigation_target:: { self , ToNav } ,
66 FilePosition , NavigationTarget , RangeInfo , TryToNav , UpmappingResult ,
77} ;
8- use hir:: { AsAssocItem , AssocItem , FileRange , InFile , MacroFileIdExt , ModuleDef , Semantics } ;
8+ use hir:: { AsAssocItem , AssocItem , FileRange , Impl , InFile , MacroFileIdExt , ModuleDef , Semantics } ;
99use ide_db:: {
1010 base_db:: { AnchoredPath , FileLoader , SourceDatabase } ,
1111 defs:: { Definition , IdentClass } ,
12+ famous_defs:: FamousDefs ,
1213 helpers:: pick_best_token,
1314 RootDatabase , SymbolKind ,
1415} ;
@@ -81,6 +82,10 @@ pub(crate) fn goto_definition(
8182 return Some ( RangeInfo :: new ( original_token. text_range ( ) , navs) ) ;
8283 }
8384
85+ if let Some ( navs) = find_from_definition ( file_id, & original_token, sema) {
86+ return Some ( RangeInfo :: new ( original_token. text_range ( ) , navs) ) ;
87+ }
88+
8489 let navs = sema
8590 . descend_into_macros_no_opaque ( original_token. clone ( ) )
8691 . into_iter ( )
@@ -125,6 +130,62 @@ pub(crate) fn goto_definition(
125130 Some ( RangeInfo :: new ( original_token. text_range ( ) , navs) )
126131}
127132
133+ // If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr.
134+ fn find_from_definition (
135+ file_id : FileId ,
136+ original_token : & SyntaxToken ,
137+ sema : & Semantics < ' _ , RootDatabase > ,
138+ ) -> Option < Vec < NavigationTarget > > {
139+ let db = sema. db ;
140+ let krate = sema. file_to_module_def ( file_id) ?. krate ( ) ;
141+
142+ // e.g. if the method call is let b = a.into(),
143+ // - receiver_type is A (type of a)
144+ // - return_type is B (type of b)
145+ // We will find the definition of B::from(a: A).
146+ let method_call = ast:: MethodCallExpr :: cast ( original_token. parent ( ) ?. parent ( ) ?) ?;
147+ let receiver_type = sema. type_of_expr ( & method_call. receiver ( ) ?) ?. original ( ) ;
148+ let return_type = sema. type_of_expr ( & method_call. clone ( ) . into ( ) ) ?. original ( ) ;
149+
150+ let ( search_method, search_trait, return_type) = match method_call. name_ref ( ) ?. text ( ) . as_str ( ) {
151+ "into" => ( "from" , FamousDefs ( sema, krate) . core_convert_From ( ) ?, return_type) ,
152+ // If the mthod is try_into() or parse(), return_type is Result<T, Error>.
153+ // Get T from type arguments of Result<T, Error>.
154+ "try_into" => (
155+ "try_from" ,
156+ FamousDefs ( sema, krate) . core_convert_TryFrom ( ) ?,
157+ return_type. type_arguments ( ) . next ( ) ?,
158+ ) ,
159+ "parse" => (
160+ "from_str" ,
161+ FamousDefs ( sema, krate) . core_str_FromStr ( ) ?,
162+ return_type. type_arguments ( ) . next ( ) ?,
163+ ) ,
164+ _ => return None ,
165+ } ;
166+
167+ let from_impls = Impl :: all_for_type ( db, return_type)
168+ . into_iter ( )
169+ . filter ( |impl_| impl_. trait_ ( db) . is_some_and ( |trait_| trait_ == search_trait) ) ;
170+ let from_methods = from_impls. flat_map ( |impl_| impl_. items ( db) ) . filter_map ( |item| match item {
171+ AssocItem :: Function ( function) if function. name ( db) . as_str ( ) == search_method => {
172+ Some ( function)
173+ }
174+ _ => None ,
175+ } ) ;
176+ let target_method = from_methods. into_iter ( ) . find ( |method| {
177+ let args = method. assoc_fn_params ( db) ;
178+
179+ // FIXME: This condition does not work for complicated cases such as
180+ // receiver_type: Vec<i64>
181+ // arg.ty(): T: IntoIterator<Item = i64>
182+ args. get ( 0 ) . is_some_and ( |arg| receiver_type. could_coerce_to ( db, arg. ty ( ) ) )
183+ } ) ?;
184+
185+ let def = Definition :: from ( target_method) ;
186+ Some ( def_to_nav ( db, def) )
187+ }
188+
128189fn try_lookup_include_path (
129190 sema : & Semantics < ' _ , RootDatabase > ,
130191 token : ast:: String ,
@@ -3022,4 +3083,27 @@ fn foo() {
30223083"# ,
30233084 ) ;
30243085 }
3086+ #[ test]
3087+ fn into_call_to_from_definition ( ) {
3088+ check (
3089+ r#"
3090+ //- minicore: from
3091+ struct A;
3092+
3093+ struct B;
3094+
3095+ impl From<A> for B {
3096+ fn from(value: A) -> Self {
3097+ //^^^^
3098+ B
3099+ }
3100+ }
3101+
3102+ fn f() {
3103+ let a = A;
3104+ let b: B = a.into$0();
3105+ }
3106+ "# ,
3107+ ) ;
3108+ }
30253109}
0 commit comments