1+ use hir:: HirDisplay ;
12use syntax:: {
2- ast:: { Expr , GenericArg } ,
3+ ast:: { Expr , GenericArg , GenericArgList } ,
34 ast:: { LetStmt , Type :: InferType } ,
45 AstNode , TextRange ,
56} ;
@@ -34,21 +35,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
3435
3536 let initializer = let_stmt. initializer ( ) ?;
3637
37- let generic_args = match & initializer {
38- Expr :: MethodCallExpr ( ce) => ce. generic_arg_list ( ) ?,
39- Expr :: CallExpr ( ce) => {
40- if let Expr :: PathExpr ( pe) = ce. expr ( ) ? {
41- pe. path ( ) ?. segment ( ) ?. generic_arg_list ( ) ?
42- } else {
43- cov_mark:: hit!( not_applicable_if_non_path_function_call) ;
44- return None ;
45- }
46- }
47- _ => {
48- cov_mark:: hit!( not_applicable_if_non_function_call_initializer) ;
49- return None ;
50- }
51- } ;
38+ let generic_args = generic_arg_list ( & initializer) ?;
5239
5340 // Find range of ::<_>
5441 let colon2 = generic_args. coloncolon_token ( ) ?;
@@ -65,7 +52,16 @@ pub(crate) fn replace_turbofish_with_explicit_type(
6552
6653 // An improvement would be to check that this is correctly part of the return value of the
6754 // function call, or sub in the actual return type.
68- let turbofish_type = & turbofish_args[ 0 ] ;
55+ let returned_type = match ctx. sema . type_of_expr ( & initializer) {
56+ Some ( returned_type) if !returned_type. original . contains_unknown ( ) => {
57+ let module = ctx. sema . scope ( let_stmt. syntax ( ) ) ?. module ( ) ;
58+ returned_type. original . display_source_code ( ctx. db ( ) , module. into ( ) ) . ok ( ) ?
59+ }
60+ _ => {
61+ cov_mark:: hit!( fallback_to_turbofish_type_if_type_info_not_available) ;
62+ turbofish_args[ 0 ] . to_string ( )
63+ }
64+ } ;
6965
7066 let initializer_start = initializer. syntax ( ) . text_range ( ) . start ( ) ;
7167 if ctx. offset ( ) > turbofish_range. end ( ) || ctx. offset ( ) < initializer_start {
@@ -83,7 +79,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
8379 "Replace turbofish with explicit type" ,
8480 TextRange :: new ( initializer_start, turbofish_range. end ( ) ) ,
8581 |builder| {
86- builder. insert ( ident_range. end ( ) , format ! ( ": {}" , turbofish_type ) ) ;
82+ builder. insert ( ident_range. end ( ) , format ! ( ": {}" , returned_type ) ) ;
8783 builder. delete ( turbofish_range) ;
8884 } ,
8985 ) ;
@@ -98,7 +94,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
9894 "Replace `_` with turbofish type" ,
9995 turbofish_range,
10096 |builder| {
101- builder. replace ( underscore_range, turbofish_type . to_string ( ) ) ;
97+ builder. replace ( underscore_range, returned_type ) ;
10298 builder. delete ( turbofish_range) ;
10399 } ,
104100 ) ;
@@ -107,6 +103,26 @@ pub(crate) fn replace_turbofish_with_explicit_type(
107103 None
108104}
109105
106+ fn generic_arg_list ( expr : & Expr ) -> Option < GenericArgList > {
107+ match expr {
108+ Expr :: MethodCallExpr ( expr) => expr. generic_arg_list ( ) ,
109+ Expr :: CallExpr ( expr) => {
110+ if let Expr :: PathExpr ( pe) = expr. expr ( ) ? {
111+ pe. path ( ) ?. segment ( ) ?. generic_arg_list ( )
112+ } else {
113+ cov_mark:: hit!( not_applicable_if_non_path_function_call) ;
114+ return None ;
115+ }
116+ }
117+ Expr :: AwaitExpr ( expr) => generic_arg_list ( & expr. expr ( ) ?) ,
118+ Expr :: TryExpr ( expr) => generic_arg_list ( & expr. expr ( ) ?) ,
119+ _ => {
120+ cov_mark:: hit!( not_applicable_if_non_function_call_initializer) ;
121+ None
122+ }
123+ }
124+ }
125+
110126#[ cfg( test) ]
111127mod tests {
112128 use super :: * ;
@@ -115,6 +131,7 @@ mod tests {
115131
116132 #[ test]
117133 fn replaces_turbofish_for_vec_string ( ) {
134+ cov_mark:: check!( fallback_to_turbofish_type_if_type_info_not_available) ;
118135 check_assist (
119136 replace_turbofish_with_explicit_type,
120137 r#"
@@ -135,6 +152,7 @@ fn main() {
135152 #[ test]
136153 fn replaces_method_calls ( ) {
137154 // foo.make() is a method call which uses a different expr in the let initializer
155+ cov_mark:: check!( fallback_to_turbofish_type_if_type_info_not_available) ;
138156 check_assist (
139157 replace_turbofish_with_explicit_type,
140158 r#"
@@ -237,6 +255,110 @@ fn make<T>() -> T {}
237255fn main() {
238256 let a = make$0::<Vec<String>, i32>();
239257}
258+ "# ,
259+ ) ;
260+ }
261+
262+ #[ test]
263+ fn replaces_turbofish_for_known_type ( ) {
264+ check_assist (
265+ replace_turbofish_with_explicit_type,
266+ r#"
267+ fn make<T>() -> T {}
268+ fn main() {
269+ let a = make$0::<i32>();
270+ }
271+ "# ,
272+ r#"
273+ fn make<T>() -> T {}
274+ fn main() {
275+ let a: i32 = make();
276+ }
277+ "# ,
278+ ) ;
279+ check_assist (
280+ replace_turbofish_with_explicit_type,
281+ r#"
282+ //- minicore: option
283+ fn make<T>() -> T {}
284+ fn main() {
285+ let a = make$0::<Option<bool>>();
286+ }
287+ "# ,
288+ r#"
289+ fn make<T>() -> T {}
290+ fn main() {
291+ let a: Option<bool> = make();
292+ }
293+ "# ,
294+ ) ;
295+ }
296+
297+ #[ test]
298+ fn replaces_turbofish_not_same_type ( ) {
299+ check_assist (
300+ replace_turbofish_with_explicit_type,
301+ r#"
302+ //- minicore: option
303+ fn make<T>() -> Option<T> {}
304+ fn main() {
305+ let a = make$0::<u128>();
306+ }
307+ "# ,
308+ r#"
309+ fn make<T>() -> Option<T> {}
310+ fn main() {
311+ let a: Option<u128> = make();
312+ }
313+ "# ,
314+ ) ;
315+ }
316+
317+ #[ test]
318+ fn replaces_turbofish_for_type_with_defaulted_generic_param ( ) {
319+ check_assist (
320+ replace_turbofish_with_explicit_type,
321+ r#"
322+ struct HasDefault<T, U = i32>(T, U);
323+ fn make<T>() -> HasDefault<T> {}
324+ fn main() {
325+ let a = make$0::<bool>();
326+ }
327+ "# ,
328+ r#"
329+ struct HasDefault<T, U = i32>(T, U);
330+ fn make<T>() -> HasDefault<T> {}
331+ fn main() {
332+ let a: HasDefault<bool> = make();
333+ }
334+ "# ,
335+ ) ;
336+ }
337+
338+ #[ test]
339+ fn replaces_turbofish_try_await ( ) {
340+ check_assist (
341+ replace_turbofish_with_explicit_type,
342+ r#"
343+ //- minicore: option, future
344+ struct Fut<T>(T);
345+ impl<T> core::future::Future for Fut<T> {
346+ type Output = Option<T>;
347+ }
348+ fn make<T>() -> Fut<T> {}
349+ fn main() {
350+ let a = make$0::<bool>().await?;
351+ }
352+ "# ,
353+ r#"
354+ struct Fut<T>(T);
355+ impl<T> core::future::Future for Fut<T> {
356+ type Output = Option<T>;
357+ }
358+ fn make<T>() -> Fut<T> {}
359+ fn main() {
360+ let a: bool = make().await?;
361+ }
240362"# ,
241363 ) ;
242364 }
0 commit comments