1
+ use hir:: HirDisplay ;
1
2
use syntax:: {
2
- ast:: { Expr , GenericArg } ,
3
+ ast:: { Expr , GenericArg , GenericArgList } ,
3
4
ast:: { LetStmt , Type :: InferType } ,
4
5
AstNode , TextRange ,
5
6
} ;
@@ -34,21 +35,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
34
35
35
36
let initializer = let_stmt. initializer ( ) ?;
36
37
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) ?;
52
39
53
40
// Find range of ::<_>
54
41
let colon2 = generic_args. coloncolon_token ( ) ?;
@@ -65,7 +52,16 @@ pub(crate) fn replace_turbofish_with_explicit_type(
65
52
66
53
// An improvement would be to check that this is correctly part of the return value of the
67
54
// 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
+ } ;
69
65
70
66
let initializer_start = initializer. syntax ( ) . text_range ( ) . start ( ) ;
71
67
if ctx. offset ( ) > turbofish_range. end ( ) || ctx. offset ( ) < initializer_start {
@@ -83,7 +79,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
83
79
"Replace turbofish with explicit type" ,
84
80
TextRange :: new ( initializer_start, turbofish_range. end ( ) ) ,
85
81
|builder| {
86
- builder. insert ( ident_range. end ( ) , format ! ( ": {}" , turbofish_type ) ) ;
82
+ builder. insert ( ident_range. end ( ) , format ! ( ": {}" , returned_type ) ) ;
87
83
builder. delete ( turbofish_range) ;
88
84
} ,
89
85
) ;
@@ -98,7 +94,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
98
94
"Replace `_` with turbofish type" ,
99
95
turbofish_range,
100
96
|builder| {
101
- builder. replace ( underscore_range, turbofish_type . to_string ( ) ) ;
97
+ builder. replace ( underscore_range, returned_type ) ;
102
98
builder. delete ( turbofish_range) ;
103
99
} ,
104
100
) ;
@@ -107,6 +103,26 @@ pub(crate) fn replace_turbofish_with_explicit_type(
107
103
None
108
104
}
109
105
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
+
110
126
#[ cfg( test) ]
111
127
mod tests {
112
128
use super :: * ;
@@ -115,6 +131,7 @@ mod tests {
115
131
116
132
#[ test]
117
133
fn replaces_turbofish_for_vec_string ( ) {
134
+ cov_mark:: check!( fallback_to_turbofish_type_if_type_info_not_available) ;
118
135
check_assist (
119
136
replace_turbofish_with_explicit_type,
120
137
r#"
@@ -135,6 +152,7 @@ fn main() {
135
152
#[ test]
136
153
fn replaces_method_calls ( ) {
137
154
// 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) ;
138
156
check_assist (
139
157
replace_turbofish_with_explicit_type,
140
158
r#"
@@ -237,6 +255,110 @@ fn make<T>() -> T {}
237
255
fn main() {
238
256
let a = make$0::<Vec<String>, i32>();
239
257
}
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
+ }
240
362
"# ,
241
363
) ;
242
364
}
0 commit comments