@@ -9,6 +9,14 @@ use syntax::{
9
9
ted, SyntaxNode ,
10
10
} ;
11
11
12
+ #[ derive( Default ) ]
13
+ struct Substs {
14
+ types : Vec < ast:: TypeArg > ,
15
+ lifetimes : Vec < ast:: LifetimeArg > ,
16
+ }
17
+
18
+ type LifetimeName = String ;
19
+
12
20
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
13
21
///
14
22
/// This is mostly useful for IDE code generation. If you paste some existing
@@ -34,7 +42,7 @@ use syntax::{
34
42
/// ```
35
43
pub struct PathTransform < ' a > {
36
44
generic_def : Option < hir:: GenericDef > ,
37
- substs : Vec < ast :: Type > ,
45
+ substs : Substs ,
38
46
target_scope : & ' a SemanticsScope < ' a > ,
39
47
source_scope : & ' a SemanticsScope < ' a > ,
40
48
}
@@ -72,7 +80,7 @@ impl<'a> PathTransform<'a> {
72
80
target_scope : & ' a SemanticsScope < ' a > ,
73
81
source_scope : & ' a SemanticsScope < ' a > ,
74
82
) -> PathTransform < ' a > {
75
- PathTransform { source_scope, target_scope, generic_def : None , substs : Vec :: new ( ) }
83
+ PathTransform { source_scope, target_scope, generic_def : None , substs : Substs :: default ( ) }
76
84
}
77
85
78
86
pub fn apply ( & self , syntax : & SyntaxNode ) {
@@ -91,11 +99,11 @@ impl<'a> PathTransform<'a> {
91
99
let target_module = self . target_scope . module ( ) ;
92
100
let source_module = self . source_scope . module ( ) ;
93
101
let skip = match self . generic_def {
94
- // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
102
+ // this is a trait impl, so we need to skip the first type parameter (i.e. Self) -- this is a bit hacky
95
103
Some ( hir:: GenericDef :: Trait ( _) ) => 1 ,
96
104
_ => 0 ,
97
105
} ;
98
- let substs_by_param : FxHashMap < _ , _ > = self
106
+ let type_substs : FxHashMap < _ , _ > = self
99
107
. generic_def
100
108
. into_iter ( )
101
109
. flat_map ( |it| it. type_params ( db) )
@@ -106,31 +114,35 @@ impl<'a> PathTransform<'a> {
106
114
// can still hit those trailing values and check if they actually have
107
115
// a default type. If they do, go for that type from `hir` to `ast` so
108
116
// the resulting change can be applied correctly.
109
- . zip ( self . substs . iter ( ) . map ( Some ) . chain ( std:: iter:: repeat ( None ) ) )
117
+ . zip ( self . substs . types . iter ( ) . map ( Some ) . chain ( std:: iter:: repeat ( None ) ) )
110
118
. filter_map ( |( k, v) | match k. split ( db) {
111
- Either :: Left ( _) => None ,
119
+ Either :: Left ( _) => None , // FIXME: map const types too
112
120
Either :: Right ( t) => match v {
113
- Some ( v) => Some ( ( k, v. clone ( ) ) ) ,
121
+ Some ( v) => Some ( ( k, v. ty ( ) ? . clone ( ) ) ) ,
114
122
None => {
115
123
let default = t. default ( db) ?;
116
- Some ( (
117
- k,
118
- ast:: make:: ty (
119
- & default
120
- . display_source_code ( db, source_module. into ( ) , false )
121
- . ok ( ) ?,
122
- ) ,
123
- ) )
124
+ let v = ast:: make:: ty (
125
+ & default. display_source_code ( db, source_module. into ( ) , false ) . ok ( ) ?,
126
+ ) ;
127
+ Some ( ( k, v) )
124
128
}
125
129
} ,
126
130
} )
127
131
. collect ( ) ;
128
- Ctx { substs : substs_by_param, target_module, source_scope : self . source_scope }
132
+ let lifetime_substs: FxHashMap < _ , _ > = self
133
+ . generic_def
134
+ . into_iter ( )
135
+ . flat_map ( |it| it. lifetime_params ( db) )
136
+ . zip ( self . substs . lifetimes . clone ( ) )
137
+ . filter_map ( |( k, v) | Some ( ( k. name ( db) . to_string ( ) , v. lifetime ( ) ?) ) )
138
+ . collect ( ) ;
139
+ Ctx { type_substs, lifetime_substs, target_module, source_scope : self . source_scope }
129
140
}
130
141
}
131
142
132
143
struct Ctx < ' a > {
133
- substs : FxHashMap < hir:: TypeOrConstParam , ast:: Type > ,
144
+ type_substs : FxHashMap < hir:: TypeOrConstParam , ast:: Type > ,
145
+ lifetime_substs : FxHashMap < LifetimeName , ast:: Lifetime > ,
134
146
target_module : hir:: Module ,
135
147
source_scope : & ' a SemanticsScope < ' a > ,
136
148
}
@@ -152,7 +164,24 @@ impl<'a> Ctx<'a> {
152
164
for path in paths {
153
165
self . transform_path ( path) ;
154
166
}
167
+
168
+ item. preorder ( )
169
+ . filter_map ( |event| match event {
170
+ syntax:: WalkEvent :: Enter ( _) => None ,
171
+ syntax:: WalkEvent :: Leave ( node) => Some ( node) ,
172
+ } )
173
+ . filter_map ( ast:: Lifetime :: cast)
174
+ . for_each ( |lifetime| {
175
+ if let Some ( subst) = self . lifetime_substs . get ( & lifetime. syntax ( ) . text ( ) . to_string ( ) )
176
+ {
177
+ ted:: replace (
178
+ lifetime. syntax ( ) ,
179
+ subst. clone_subtree ( ) . clone_for_update ( ) . syntax ( ) ,
180
+ ) ;
181
+ }
182
+ } ) ;
155
183
}
184
+
156
185
fn transform_path ( & self , path : ast:: Path ) -> Option < ( ) > {
157
186
if path. qualifier ( ) . is_some ( ) {
158
187
return None ;
@@ -169,7 +198,7 @@ impl<'a> Ctx<'a> {
169
198
170
199
match resolution {
171
200
hir:: PathResolution :: TypeParam ( tp) => {
172
- if let Some ( subst) = self . substs . get ( & tp. merge ( ) ) {
201
+ if let Some ( subst) = self . type_substs . get ( & tp. merge ( ) ) {
173
202
let parent = path. syntax ( ) . parent ( ) ?;
174
203
if let Some ( parent) = ast:: Path :: cast ( parent. clone ( ) ) {
175
204
// Path inside path means that there is an associated
@@ -250,7 +279,7 @@ impl<'a> Ctx<'a> {
250
279
251
280
// FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
252
281
// trait ref, and then go from the types in the substs back to the syntax).
253
- fn get_syntactic_substs ( impl_def : ast:: Impl ) -> Option < Vec < ast :: Type > > {
282
+ fn get_syntactic_substs ( impl_def : ast:: Impl ) -> Option < Substs > {
254
283
let target_trait = impl_def. trait_ ( ) ?;
255
284
let path_type = match target_trait {
256
285
ast:: Type :: PathType ( path) => path,
@@ -261,13 +290,13 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
261
290
get_type_args_from_arg_list ( generic_arg_list)
262
291
}
263
292
264
- fn get_type_args_from_arg_list ( generic_arg_list : ast:: GenericArgList ) -> Option < Vec < ast :: Type > > {
265
- let mut result = Vec :: new ( ) ;
266
- for generic_arg in generic_arg_list. generic_args ( ) {
267
- if let ast:: GenericArg :: TypeArg ( type_arg) = generic_arg {
268
- result. push ( type_arg . ty ( ) ? )
269
- }
270
- }
293
+ fn get_type_args_from_arg_list ( generic_arg_list : ast:: GenericArgList ) -> Option < Substs > {
294
+ let mut result = Substs :: default ( ) ;
295
+ generic_arg_list. generic_args ( ) . for_each ( |generic_arg| match generic_arg {
296
+ ast:: GenericArg :: TypeArg ( type_arg) => result . types . push ( type_arg ) ,
297
+ ast :: GenericArg :: LifetimeArg ( l_arg ) => result. lifetimes . push ( l_arg ) ,
298
+ _ => ( ) , // FIXME: don't filter out const params
299
+ } ) ;
271
300
272
301
Some ( result)
273
302
}
0 commit comments