1
+ use hir:: { Semantics , TypeParam } ;
2
+ use ide_db:: {
3
+ base_db:: { FileId , FileRange } ,
4
+ defs:: Definition ,
5
+ search:: SearchScope ,
6
+ RootDatabase ,
7
+ } ;
1
8
use syntax:: {
2
9
ast:: { self , make:: impl_trait_type, HasGenericParams , HasName , HasTypeBounds } ,
3
10
ted, AstNode ,
@@ -22,13 +29,12 @@ pub(crate) fn replace_named_generic_with_impl(
22
29
) -> Option < ( ) > {
23
30
// finds `<P: AsRef<Path>>`
24
31
let type_param = ctx. find_node_at_offset :: < ast:: TypeParam > ( ) ?;
32
+ // returns `P`
33
+ let type_param_name = type_param. name ( ) ?;
25
34
26
35
// The list of type bounds / traits: `AsRef<Path>`
27
36
let type_bound_list = type_param. type_bound_list ( ) ?;
28
37
29
- // returns `P`
30
- let type_param_name = type_param. name ( ) ?;
31
-
32
38
let fn_ = type_param. syntax ( ) . ancestors ( ) . find_map ( ast:: Fn :: cast) ?;
33
39
let params = fn_
34
40
. param_list ( ) ?
@@ -53,6 +59,11 @@ pub(crate) fn replace_named_generic_with_impl(
53
59
return None ;
54
60
}
55
61
62
+ let type_param_hir_def = ctx. sema . to_def ( & type_param) ?;
63
+ if is_referenced_outside ( ctx. db ( ) , type_param_hir_def, & fn_, ctx. file_id ( ) ) {
64
+ return None ;
65
+ }
66
+
56
67
let target = type_param. syntax ( ) . text_range ( ) ;
57
68
58
69
acc. add (
@@ -88,11 +99,36 @@ pub(crate) fn replace_named_generic_with_impl(
88
99
)
89
100
}
90
101
102
+ fn is_referenced_outside (
103
+ db : & RootDatabase ,
104
+ type_param : TypeParam ,
105
+ fn_ : & ast:: Fn ,
106
+ file_id : FileId ,
107
+ ) -> bool {
108
+ let semantics = Semantics :: new ( db) ;
109
+ let type_param_def = Definition :: GenericParam ( hir:: GenericParam :: TypeParam ( type_param) ) ;
110
+
111
+ // limit search scope to function body & return type
112
+ let search_ranges = vec ! [
113
+ fn_. body( ) . map( |body| body. syntax( ) . text_range( ) ) ,
114
+ fn_. ret_type( ) . map( |ret_type| ret_type. syntax( ) . text_range( ) ) ,
115
+ ] ;
116
+
117
+ search_ranges. into_iter ( ) . filter_map ( |search_range| search_range) . any ( |search_range| {
118
+ let file_range = FileRange { file_id, range : search_range } ;
119
+ !type_param_def
120
+ . usages ( & semantics)
121
+ . in_scope ( SearchScope :: file_range ( file_range) )
122
+ . all ( )
123
+ . is_empty ( )
124
+ } )
125
+ }
126
+
91
127
#[ cfg( test) ]
92
128
mod tests {
93
129
use super :: * ;
94
130
95
- use crate :: tests:: check_assist;
131
+ use crate :: tests:: { check_assist, check_assist_not_applicable } ;
96
132
97
133
#[ test]
98
134
fn replace_generic_moves_into_function ( ) {
@@ -122,12 +158,22 @@ mod tests {
122
158
}
123
159
124
160
#[ test]
125
- fn replace_generic_with_multiple_generic_names ( ) {
161
+ fn replace_generic_with_multiple_generic_params ( ) {
126
162
check_assist (
127
163
replace_named_generic_with_impl,
128
164
r#"fn new<P: AsRef<Path>, T$0: ToString>(t: T, p: P) -> Self {}"# ,
129
165
r#"fn new<P: AsRef<Path>>(t: impl ToString, p: P) -> Self {}"# ,
130
166
) ;
167
+ check_assist (
168
+ replace_named_generic_with_impl,
169
+ r#"fn new<T$0: ToString, P: AsRef<Path>>(t: T, p: P) -> Self {}"# ,
170
+ r#"fn new<P: AsRef<Path>>(t: impl ToString, p: P) -> Self {}"# ,
171
+ ) ;
172
+ check_assist (
173
+ replace_named_generic_with_impl,
174
+ r#"fn new<A: Send, B$0: ToString, C: Debug>(a: A, b: B, c: C) -> Self {}"# ,
175
+ r#"fn new<A: Send, C: Debug>(a: A, b: impl ToString, c: C) -> Self {}"# ,
176
+ ) ;
131
177
}
132
178
133
179
#[ test]
@@ -138,4 +184,35 @@ mod tests {
138
184
r#"fn new(p: impl Send + Sync) -> Self {}"# ,
139
185
) ;
140
186
}
187
+
188
+ #[ test]
189
+ fn replace_generic_not_applicable_if_param_used_as_return_type ( ) {
190
+ check_assist_not_applicable (
191
+ replace_named_generic_with_impl,
192
+ r#"fn new<P$0: Send + Sync>(p: P) -> P {}"# ,
193
+ ) ;
194
+ }
195
+
196
+ #[ test]
197
+ fn replace_generic_not_applicable_if_param_used_in_fn_body ( ) {
198
+ check_assist_not_applicable (
199
+ replace_named_generic_with_impl,
200
+ r#"fn new<P$0: ToString>(p: P) { let x: &dyn P = &O; }"# ,
201
+ ) ;
202
+ }
203
+
204
+ #[ test]
205
+ fn replace_generic_ignores_another_function_with_same_param_type ( ) {
206
+ check_assist (
207
+ replace_named_generic_with_impl,
208
+ r#"
209
+ fn new<P$0: Send + Sync>(p: P) {}
210
+ fn hello<P: Debug>(p: P) { println!("{:?}", p); }
211
+ "# ,
212
+ r#"
213
+ fn new(p: impl Send + Sync) {}
214
+ fn hello<P: Debug>(p: P) { println!("{:?}", p); }
215
+ "# ,
216
+ ) ;
217
+ }
141
218
}
0 commit comments