1+ use hir:: { Semantics , TypeParam } ;
2+ use ide_db:: {
3+ base_db:: { FileId , FileRange } ,
4+ defs:: Definition ,
5+ search:: SearchScope ,
6+ RootDatabase ,
7+ } ;
18use syntax:: {
29 ast:: { self , make:: impl_trait_type, HasGenericParams , HasName , HasTypeBounds } ,
310 ted, AstNode ,
@@ -22,13 +29,12 @@ pub(crate) fn replace_named_generic_with_impl(
2229) -> Option < ( ) > {
2330 // finds `<P: AsRef<Path>>`
2431 let type_param = ctx. find_node_at_offset :: < ast:: TypeParam > ( ) ?;
32+ // returns `P`
33+ let type_param_name = type_param. name ( ) ?;
2534
2635 // The list of type bounds / traits: `AsRef<Path>`
2736 let type_bound_list = type_param. type_bound_list ( ) ?;
2837
29- // returns `P`
30- let type_param_name = type_param. name ( ) ?;
31-
3238 let fn_ = type_param. syntax ( ) . ancestors ( ) . find_map ( ast:: Fn :: cast) ?;
3339 let params = fn_
3440 . param_list ( ) ?
@@ -53,6 +59,11 @@ pub(crate) fn replace_named_generic_with_impl(
5359 return None ;
5460 }
5561
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+
5667 let target = type_param. syntax ( ) . text_range ( ) ;
5768
5869 acc. add (
@@ -88,11 +99,36 @@ pub(crate) fn replace_named_generic_with_impl(
8899 )
89100}
90101
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+
91127#[ cfg( test) ]
92128mod tests {
93129 use super :: * ;
94130
95- use crate :: tests:: check_assist;
131+ use crate :: tests:: { check_assist, check_assist_not_applicable } ;
96132
97133 #[ test]
98134 fn replace_generic_moves_into_function ( ) {
@@ -122,12 +158,22 @@ mod tests {
122158 }
123159
124160 #[ test]
125- fn replace_generic_with_multiple_generic_names ( ) {
161+ fn replace_generic_with_multiple_generic_params ( ) {
126162 check_assist (
127163 replace_named_generic_with_impl,
128164 r#"fn new<P: AsRef<Path>, T$0: ToString>(t: T, p: P) -> Self {}"# ,
129165 r#"fn new<P: AsRef<Path>>(t: impl ToString, p: P) -> Self {}"# ,
130166 ) ;
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+ ) ;
131177 }
132178
133179 #[ test]
@@ -138,4 +184,35 @@ mod tests {
138184 r#"fn new(p: impl Send + Sync) -> Self {}"# ,
139185 ) ;
140186 }
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+ }
141218}
0 commit comments