@@ -9,6 +9,14 @@ use syntax::{
99 ted, SyntaxNode ,
1010} ;
1111
12+ #[ derive( Default ) ]
13+ struct Substs {
14+ types : Vec < ast:: TypeArg > ,
15+ lifetimes : Vec < ast:: LifetimeArg > ,
16+ }
17+
18+ type LifetimeName = String ;
19+
1220/// `PathTransform` substitutes path in SyntaxNodes in bulk.
1321///
1422/// This is mostly useful for IDE code generation. If you paste some existing
@@ -34,7 +42,7 @@ use syntax::{
3442/// ```
3543pub struct PathTransform < ' a > {
3644 generic_def : Option < hir:: GenericDef > ,
37- substs : Vec < ast :: Type > ,
45+ substs : Substs ,
3846 target_scope : & ' a SemanticsScope < ' a > ,
3947 source_scope : & ' a SemanticsScope < ' a > ,
4048}
@@ -72,7 +80,7 @@ impl<'a> PathTransform<'a> {
7280 target_scope : & ' a SemanticsScope < ' a > ,
7381 source_scope : & ' a SemanticsScope < ' a > ,
7482 ) -> 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 ( ) }
7684 }
7785
7886 pub fn apply ( & self , syntax : & SyntaxNode ) {
@@ -91,11 +99,11 @@ impl<'a> PathTransform<'a> {
9199 let target_module = self . target_scope . module ( ) ;
92100 let source_module = self . source_scope . module ( ) ;
93101 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
95103 Some ( hir:: GenericDef :: Trait ( _) ) => 1 ,
96104 _ => 0 ,
97105 } ;
98- let substs_by_param : FxHashMap < _ , _ > = self
106+ let type_substs : FxHashMap < _ , _ > = self
99107 . generic_def
100108 . into_iter ( )
101109 . flat_map ( |it| it. type_params ( db) )
@@ -106,31 +114,35 @@ impl<'a> PathTransform<'a> {
106114 // can still hit those trailing values and check if they actually have
107115 // a default type. If they do, go for that type from `hir` to `ast` so
108116 // 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 ) ) )
110118 . filter_map ( |( k, v) | match k. split ( db) {
111- Either :: Left ( _) => None ,
119+ Either :: Left ( _) => None , // FIXME: map const types too
112120 Either :: Right ( t) => match v {
113- Some ( v) => Some ( ( k, v. clone ( ) ) ) ,
121+ Some ( v) => Some ( ( k, v. ty ( ) ? . clone ( ) ) ) ,
114122 None => {
115123 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) )
124128 }
125129 } ,
126130 } )
127131 . 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 }
129140 }
130141}
131142
132143struct Ctx < ' a > {
133- substs : FxHashMap < hir:: TypeOrConstParam , ast:: Type > ,
144+ type_substs : FxHashMap < hir:: TypeOrConstParam , ast:: Type > ,
145+ lifetime_substs : FxHashMap < LifetimeName , ast:: Lifetime > ,
134146 target_module : hir:: Module ,
135147 source_scope : & ' a SemanticsScope < ' a > ,
136148}
@@ -152,7 +164,24 @@ impl<'a> Ctx<'a> {
152164 for path in paths {
153165 self . transform_path ( path) ;
154166 }
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+ } ) ;
155183 }
184+
156185 fn transform_path ( & self , path : ast:: Path ) -> Option < ( ) > {
157186 if path. qualifier ( ) . is_some ( ) {
158187 return None ;
@@ -169,7 +198,7 @@ impl<'a> Ctx<'a> {
169198
170199 match resolution {
171200 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 ( ) ) {
173202 let parent = path. syntax ( ) . parent ( ) ?;
174203 if let Some ( parent) = ast:: Path :: cast ( parent. clone ( ) ) {
175204 // Path inside path means that there is an associated
@@ -250,7 +279,7 @@ impl<'a> Ctx<'a> {
250279
251280// FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
252281// 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 > {
254283 let target_trait = impl_def. trait_ ( ) ?;
255284 let path_type = match target_trait {
256285 ast:: Type :: PathType ( path) => path,
@@ -261,13 +290,13 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
261290 get_type_args_from_arg_list ( generic_arg_list)
262291}
263292
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+ } ) ;
271300
272301 Some ( result)
273302}
0 commit comments