1
1
use rustc_hash:: FxHashSet ;
2
2
use syntax:: {
3
- ast:: { self , GenericParamsOwner , NameOwner } ,
4
- AstNode , TextRange , TextSize ,
3
+ ast:: { self , edit_in_place:: GenericParamsOwnerEdit , make, GenericParamsOwner } ,
4
+ ted:: { self , Position } ,
5
+ AstNode , TextRange ,
5
6
} ;
6
7
7
8
use crate :: { assist_context:: AssistBuilder , AssistContext , AssistId , AssistKind , Assists } ;
@@ -37,10 +38,12 @@ static ASSIST_LABEL: &str = "Introduce named lifetime";
37
38
pub ( crate ) fn introduce_named_lifetime ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
38
39
let lifetime =
39
40
ctx. find_node_at_offset :: < ast:: Lifetime > ( ) . filter ( |lifetime| lifetime. text ( ) == "'_" ) ?;
41
+ let lifetime_loc = lifetime. lifetime_ident_token ( ) ?. text_range ( ) ;
42
+
40
43
if let Some ( fn_def) = lifetime. syntax ( ) . ancestors ( ) . find_map ( ast:: Fn :: cast) {
41
- generate_fn_def_assist ( acc, & fn_def, lifetime. lifetime_ident_token ( ) ? . text_range ( ) )
44
+ generate_fn_def_assist ( acc, fn_def, lifetime_loc , lifetime)
42
45
} else if let Some ( impl_def) = lifetime. syntax ( ) . ancestors ( ) . find_map ( ast:: Impl :: cast) {
43
- generate_impl_def_assist ( acc, & impl_def, lifetime. lifetime_ident_token ( ) ? . text_range ( ) )
46
+ generate_impl_def_assist ( acc, impl_def, lifetime_loc , lifetime)
44
47
} else {
45
48
None
46
49
}
@@ -49,26 +52,26 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -
49
52
/// Generate the assist for the fn def case
50
53
fn generate_fn_def_assist (
51
54
acc : & mut Assists ,
52
- fn_def : & ast:: Fn ,
55
+ fn_def : ast:: Fn ,
53
56
lifetime_loc : TextRange ,
57
+ lifetime : ast:: Lifetime ,
54
58
) -> Option < ( ) > {
55
59
let param_list: ast:: ParamList = fn_def. param_list ( ) ?;
56
- let new_lifetime_param = generate_unique_lifetime_param_name ( & fn_def. generic_param_list ( ) ) ?;
57
- let end_of_fn_ident = fn_def. name ( ) ?. ident_token ( ) ?. text_range ( ) . end ( ) ;
60
+ let new_lifetime_param = generate_unique_lifetime_param_name ( fn_def. generic_param_list ( ) ) ?;
58
61
let self_param =
59
62
// use the self if it's a reference and has no explicit lifetime
60
63
param_list. self_param ( ) . filter ( |p| p. lifetime ( ) . is_none ( ) && p. amp_token ( ) . is_some ( ) ) ;
61
64
// compute the location which implicitly has the same lifetime as the anonymous lifetime
62
65
let loc_needing_lifetime = if let Some ( self_param) = self_param {
63
66
// if we have a self reference, use that
64
- Some ( self_param . name ( ) ? . syntax ( ) . text_range ( ) . start ( ) )
67
+ Some ( NeedsLifetime :: SelfParam ( self_param ) )
65
68
} else {
66
69
// otherwise, if there's a single reference parameter without a named liftime, use that
67
70
let fn_params_without_lifetime: Vec < _ > = param_list
68
71
. params ( )
69
72
. filter_map ( |param| match param. ty ( ) {
70
73
Some ( ast:: Type :: RefType ( ascribed_type) ) if ascribed_type. lifetime ( ) . is_none ( ) => {
71
- Some ( ascribed_type . amp_token ( ) ? . text_range ( ) . end ( ) )
74
+ Some ( NeedsLifetime :: RefType ( ascribed_type ) )
72
75
}
73
76
_ => None ,
74
77
} )
@@ -81,30 +84,46 @@ fn generate_fn_def_assist(
81
84
}
82
85
} ;
83
86
acc. add ( AssistId ( ASSIST_NAME , AssistKind :: Refactor ) , ASSIST_LABEL , lifetime_loc, |builder| {
84
- add_lifetime_param ( fn_def, builder, end_of_fn_ident, new_lifetime_param) ;
85
- builder. replace ( lifetime_loc, format ! ( "'{}" , new_lifetime_param) ) ;
86
- loc_needing_lifetime. map ( |loc| builder. insert ( loc, format ! ( "'{} " , new_lifetime_param) ) ) ;
87
+ let fn_def = builder. make_ast_mut ( fn_def) ;
88
+ let lifetime = builder. make_ast_mut ( lifetime) ;
89
+ let loc_needing_lifetime =
90
+ loc_needing_lifetime. and_then ( |it| it. make_mut ( builder) . to_position ( ) ) ;
91
+
92
+ add_lifetime_param ( fn_def. get_or_create_generic_param_list ( ) , new_lifetime_param) ;
93
+ ted:: replace (
94
+ lifetime. syntax ( ) ,
95
+ make_ast_lifetime ( new_lifetime_param) . clone_for_update ( ) . syntax ( ) ,
96
+ ) ;
97
+ loc_needing_lifetime. map ( |position| {
98
+ ted:: insert ( position, make_ast_lifetime ( new_lifetime_param) . clone_for_update ( ) . syntax ( ) )
99
+ } ) ;
87
100
} )
88
101
}
89
102
90
103
/// Generate the assist for the impl def case
91
104
fn generate_impl_def_assist (
92
105
acc : & mut Assists ,
93
- impl_def : & ast:: Impl ,
106
+ impl_def : ast:: Impl ,
94
107
lifetime_loc : TextRange ,
108
+ lifetime : ast:: Lifetime ,
95
109
) -> Option < ( ) > {
96
- let new_lifetime_param = generate_unique_lifetime_param_name ( & impl_def. generic_param_list ( ) ) ?;
97
- let end_of_impl_kw = impl_def. impl_token ( ) ?. text_range ( ) . end ( ) ;
110
+ let new_lifetime_param = generate_unique_lifetime_param_name ( impl_def. generic_param_list ( ) ) ?;
98
111
acc. add ( AssistId ( ASSIST_NAME , AssistKind :: Refactor ) , ASSIST_LABEL , lifetime_loc, |builder| {
99
- add_lifetime_param ( impl_def, builder, end_of_impl_kw, new_lifetime_param) ;
100
- builder. replace ( lifetime_loc, format ! ( "'{}" , new_lifetime_param) ) ;
112
+ let impl_def = builder. make_ast_mut ( impl_def) ;
113
+ let lifetime = builder. make_ast_mut ( lifetime) ;
114
+
115
+ add_lifetime_param ( impl_def. get_or_create_generic_param_list ( ) , new_lifetime_param) ;
116
+ ted:: replace (
117
+ lifetime. syntax ( ) ,
118
+ make_ast_lifetime ( new_lifetime_param) . clone_for_update ( ) . syntax ( ) ,
119
+ ) ;
101
120
} )
102
121
}
103
122
104
123
/// Given a type parameter list, generate a unique lifetime parameter name
105
124
/// which is not in the list
106
125
fn generate_unique_lifetime_param_name (
107
- existing_type_param_list : & Option < ast:: GenericParamList > ,
126
+ existing_type_param_list : Option < ast:: GenericParamList > ,
108
127
) -> Option < char > {
109
128
match existing_type_param_list {
110
129
Some ( type_params) => {
@@ -118,25 +137,37 @@ fn generate_unique_lifetime_param_name(
118
137
}
119
138
}
120
139
121
- /// Add the lifetime param to `builder`. If there are type parameters in `type_params_owner`, add it to the end. Otherwise
122
- /// add new type params brackets with the lifetime parameter at `new_type_params_loc`.
123
- fn add_lifetime_param < TypeParamsOwner : ast:: GenericParamsOwner > (
124
- type_params_owner : & TypeParamsOwner ,
125
- builder : & mut AssistBuilder ,
126
- new_type_params_loc : TextSize ,
127
- new_lifetime_param : char ,
128
- ) {
129
- match type_params_owner. generic_param_list ( ) {
130
- // add the new lifetime parameter to an existing type param list
131
- Some ( type_params) => {
132
- builder. insert (
133
- ( u32:: from ( type_params. syntax ( ) . text_range ( ) . end ( ) ) - 1 ) . into ( ) ,
134
- format ! ( ", '{}" , new_lifetime_param) ,
135
- ) ;
140
+ fn add_lifetime_param ( type_params : ast:: GenericParamList , new_lifetime_param : char ) {
141
+ let generic_param =
142
+ make:: generic_param ( format ! ( "'{}" , new_lifetime_param) , None ) . clone_for_update ( ) ;
143
+ type_params. add_generic_param ( generic_param) ;
144
+ }
145
+
146
+ fn make_ast_lifetime ( new_lifetime_param : char ) -> ast:: Lifetime {
147
+ make:: generic_param ( format ! ( "'{}" , new_lifetime_param) , None )
148
+ . syntax ( )
149
+ . descendants ( )
150
+ . find_map ( ast:: Lifetime :: cast)
151
+ . unwrap ( )
152
+ }
153
+
154
+ enum NeedsLifetime {
155
+ SelfParam ( ast:: SelfParam ) ,
156
+ RefType ( ast:: RefType ) ,
157
+ }
158
+
159
+ impl NeedsLifetime {
160
+ fn make_mut ( self , builder : & mut AssistBuilder ) -> Self {
161
+ match self {
162
+ Self :: SelfParam ( it) => Self :: SelfParam ( builder. make_ast_mut ( it) ) ,
163
+ Self :: RefType ( it) => Self :: RefType ( builder. make_ast_mut ( it) ) ,
136
164
}
137
- // create a new type param list containing only the new lifetime parameter
138
- None => {
139
- builder. insert ( new_type_params_loc, format ! ( "<'{}>" , new_lifetime_param) ) ;
165
+ }
166
+
167
+ fn to_position ( self ) -> Option < Position > {
168
+ match self {
169
+ Self :: SelfParam ( it) => Some ( Position :: after ( it. amp_token ( ) ?) ) ,
170
+ Self :: RefType ( it) => Some ( Position :: after ( it. amp_token ( ) ?) ) ,
140
171
}
141
172
}
142
173
}
@@ -312,4 +343,13 @@ mod tests {
312
343
r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"# ,
313
344
) ;
314
345
}
346
+
347
+ #[ test]
348
+ fn test_function_add_lifetime_to_self_ref_mut ( ) {
349
+ check_assist (
350
+ introduce_named_lifetime,
351
+ r#"fn foo(&mut self) -> &'_$0 ()"# ,
352
+ r#"fn foo<'a>(&'a mut self) -> &'a ()"# ,
353
+ ) ;
354
+ }
315
355
}
0 commit comments