1
1
use syntax:: {
2
- ast:: Expr ,
2
+ ast:: { Expr , GenericArg , GenericArgList } ,
3
3
ast:: { LetStmt , Type :: InferType } ,
4
4
AstNode , TextRange ,
5
5
} ;
@@ -34,33 +34,31 @@ pub(crate) fn replace_turbofish_with_explicit_type(
34
34
35
35
let initializer = let_stmt. initializer ( ) ?;
36
36
37
- let ( turbofish_start, turbofish_type, turbofish_end) = if let Expr :: CallExpr ( ce) = initializer {
38
- if let Expr :: PathExpr ( pe) = ce. expr ( ) ? {
39
- let path = pe. path ( ) ?;
40
-
41
- let generic_args = path. segment ( ) ?. generic_arg_list ( ) ?;
42
-
43
- let colon2 = generic_args. coloncolon_token ( ) ?;
44
- let r_angle = generic_args. r_angle_token ( ) ?;
45
-
46
- let turbofish_args_as_string = generic_args
47
- . generic_args ( )
48
- . into_iter ( )
49
- . map ( |a| -> String { a. to_string ( ) } )
50
- . collect :: < Vec < String > > ( )
51
- . join ( ", " ) ;
52
-
53
- ( colon2. text_range ( ) . start ( ) , turbofish_args_as_string, r_angle. text_range ( ) . end ( ) )
54
- } else {
55
- cov_mark:: hit!( not_applicable_if_non_path_function_call) ;
37
+ let ( turbofish_range, turbofish_type) = match & initializer {
38
+ Expr :: MethodCallExpr ( ce) => {
39
+ let generic_args = ce. generic_arg_list ( ) ?;
40
+ ( turbofish_range ( & generic_args) ?, turbofish_type ( & generic_args) ?)
41
+ }
42
+ Expr :: CallExpr ( ce) => {
43
+ if let Expr :: PathExpr ( pe) = ce. expr ( ) ? {
44
+ let generic_args = pe. path ( ) ?. segment ( ) ?. generic_arg_list ( ) ?;
45
+ ( turbofish_range ( & generic_args) ?, turbofish_type ( & generic_args) ?)
46
+ } else {
47
+ cov_mark:: hit!( not_applicable_if_non_path_function_call) ;
48
+ return None ;
49
+ }
50
+ }
51
+ _ => {
52
+ cov_mark:: hit!( not_applicable_if_non_function_call_initializer) ;
56
53
return None ;
57
54
}
58
- } else {
59
- cov_mark:: hit!( not_applicable_if_non_function_call_initializer) ;
60
- return None ;
61
55
} ;
62
56
63
- let turbofish_range = TextRange :: new ( turbofish_start, turbofish_end) ;
57
+ let initializer_start = initializer. syntax ( ) . text_range ( ) . start ( ) ;
58
+ if ctx. offset ( ) > turbofish_range. end ( ) || ctx. offset ( ) < initializer_start {
59
+ cov_mark:: hit!( not_applicable_outside_turbofish) ;
60
+ return None ;
61
+ }
64
62
65
63
if let None = let_stmt. colon_token ( ) {
66
64
// If there's no colon in a let statement, then there is no explicit type.
@@ -70,7 +68,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
70
68
return acc. add (
71
69
AssistId ( "replace_turbofish_with_explicit_type" , AssistKind :: RefactorRewrite ) ,
72
70
format ! ( "Replace turbofish with explicit type `: <{}>`" , turbofish_type) ,
73
- turbofish_range,
71
+ TextRange :: new ( initializer_start , turbofish_range. end ( ) ) ,
74
72
|builder| {
75
73
builder. insert ( ident_range. end ( ) , format ! ( ": {}" , turbofish_type) ) ;
76
74
builder. delete ( turbofish_range) ;
@@ -96,6 +94,31 @@ pub(crate) fn replace_turbofish_with_explicit_type(
96
94
None
97
95
}
98
96
97
+ /// Returns the type of the turbofish as a String.
98
+ /// Returns None if there are 0 or >1 arguments.
99
+ fn turbofish_type ( generic_args : & GenericArgList ) -> Option < String > {
100
+ let turbofish_args: Vec < GenericArg > = generic_args. generic_args ( ) . into_iter ( ) . collect ( ) ;
101
+
102
+ if turbofish_args. len ( ) != 1 {
103
+ cov_mark:: hit!( not_applicable_if_not_single_arg) ;
104
+ return None ;
105
+ }
106
+
107
+ // An improvement would be to check that this is correctly part of the return value of the
108
+ // function call, or sub in the actual return type.
109
+ let turbofish_type = turbofish_args[ 0 ] . to_string ( ) ;
110
+
111
+ Some ( turbofish_type)
112
+ }
113
+
114
+ /// Returns the TextRange of the whole turbofish expression, and the generic argument as a String.
115
+ fn turbofish_range ( generic_args : & GenericArgList ) -> Option < TextRange > {
116
+ let colon2 = generic_args. coloncolon_token ( ) ?;
117
+ let r_angle = generic_args. r_angle_token ( ) ?;
118
+
119
+ Some ( TextRange :: new ( colon2. text_range ( ) . start ( ) , r_angle. text_range ( ) . end ( ) ) )
120
+ }
121
+
99
122
#[ cfg( test) ]
100
123
mod tests {
101
124
use super :: * ;
@@ -121,6 +144,26 @@ fn main() {
121
144
) ;
122
145
}
123
146
147
+ #[ test]
148
+ fn replaces_method_calls ( ) {
149
+ // foo.make() is a method call which uses a different expr in the let initializer
150
+ check_assist (
151
+ replace_turbofish_with_explicit_type,
152
+ r#"
153
+ fn make<T>() -> T {}
154
+ fn main() {
155
+ let a = foo.make$0::<Vec<String>>();
156
+ }
157
+ "# ,
158
+ r#"
159
+ fn make<T>() -> T {}
160
+ fn main() {
161
+ let a: Vec<String> = foo.make();
162
+ }
163
+ "# ,
164
+ ) ;
165
+ }
166
+
124
167
#[ test]
125
168
fn replace_turbofish_target ( ) {
126
169
check_assist_target (
@@ -131,7 +174,21 @@ fn main() {
131
174
let a = $0make::<Vec<String>>();
132
175
}
133
176
"# ,
134
- r#"::<Vec<String>>"# ,
177
+ r#"make::<Vec<String>>"# ,
178
+ ) ;
179
+ }
180
+
181
+ #[ test]
182
+ fn not_applicable_outside_turbofish ( ) {
183
+ cov_mark:: check!( not_applicable_outside_turbofish) ;
184
+ check_assist_not_applicable (
185
+ replace_turbofish_with_explicit_type,
186
+ r#"
187
+ fn make<T>() -> T {}
188
+ fn main() {
189
+ let $0a = make::<Vec<String>>();
190
+ }
191
+ "# ,
135
192
) ;
136
193
}
137
194
@@ -178,6 +235,20 @@ fn make<T>() -> T {}
178
235
fn main() {
179
236
$0let a = (|| {})();
180
237
}
238
+ "# ,
239
+ ) ;
240
+ }
241
+
242
+ #[ test]
243
+ fn non_applicable_multiple_generic_args ( ) {
244
+ cov_mark:: check!( not_applicable_if_not_single_arg) ;
245
+ check_assist_not_applicable (
246
+ replace_turbofish_with_explicit_type,
247
+ r#"
248
+ fn make<T>() -> T {}
249
+ fn main() {
250
+ let a = make$0::<Vec<String>, i32>();
251
+ }
181
252
"# ,
182
253
) ;
183
254
}
0 commit comments