1
- use std:: collections:: HashMap ;
2
-
3
- use hir:: { db:: HirDatabase , HasSource } ;
1
+ use hir:: { db:: HirDatabase , HasSource , InFile } ;
4
2
use ra_syntax:: {
5
3
ast:: { self , edit, make, AstNode , NameOwner } ,
6
4
SmolStr ,
7
5
} ;
8
6
9
- use crate :: { Assist , AssistCtx , AssistId } ;
7
+ use crate :: {
8
+ ast_transform:: { self , AstTransform , QualifyPaths , SubstituteTypeParams } ,
9
+ Assist , AssistCtx , AssistId ,
10
+ } ;
10
11
11
12
#[ derive( PartialEq ) ]
12
13
enum AddMissingImplMembersMode {
@@ -134,25 +135,23 @@ fn add_missing_impl_members_inner(
134
135
return None ;
135
136
}
136
137
137
- let file_id = ctx. frange . file_id ;
138
138
let db = ctx. db ;
139
+ let file_id = ctx. frange . file_id ;
140
+ let trait_file_id = trait_. source ( db) . file_id ;
139
141
140
142
ctx. add_assist ( AssistId ( assist_id) , label, |edit| {
141
143
let n_existing_items = impl_item_list. impl_items ( ) . count ( ) ;
142
- let substs = get_syntactic_substs ( impl_node) . unwrap_or_default ( ) ;
143
- let generic_def: hir:: GenericDef = trait_. into ( ) ;
144
- let substs_by_param: HashMap < _ , _ > = generic_def
145
- . params ( db)
146
- . into_iter ( )
147
- // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
148
- . skip ( 1 )
149
- . zip ( substs. into_iter ( ) )
150
- . collect ( ) ;
144
+ let module = hir:: SourceAnalyzer :: new (
145
+ db,
146
+ hir:: InFile :: new ( file_id. into ( ) , impl_node. syntax ( ) ) ,
147
+ None ,
148
+ )
149
+ . module ( ) ;
150
+ let ast_transform = QualifyPaths :: new ( db, module)
151
+ . or ( SubstituteTypeParams :: for_trait_impl ( db, trait_, impl_node) ) ;
151
152
let items = missing_items
152
153
. into_iter ( )
153
- . map ( |it| {
154
- substitute_type_params ( db, hir:: InFile :: new ( file_id. into ( ) , it) , & substs_by_param)
155
- } )
154
+ . map ( |it| ast_transform:: apply ( & * ast_transform, InFile :: new ( trait_file_id, it) ) )
156
155
. map ( |it| match it {
157
156
ast:: ImplItem :: FnDef ( def) => ast:: ImplItem :: FnDef ( add_body ( def) ) ,
158
157
_ => it,
@@ -177,56 +176,6 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
177
176
}
178
177
}
179
178
180
- // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
181
- // trait ref, and then go from the types in the substs back to the syntax)
182
- // FIXME: This should be a general utility (not even just for assists)
183
- fn get_syntactic_substs ( impl_block : ast:: ImplBlock ) -> Option < Vec < ast:: TypeRef > > {
184
- let target_trait = impl_block. target_trait ( ) ?;
185
- let path_type = match target_trait {
186
- ast:: TypeRef :: PathType ( path) => path,
187
- _ => return None ,
188
- } ;
189
- let type_arg_list = path_type. path ( ) ?. segment ( ) ?. type_arg_list ( ) ?;
190
- let mut result = Vec :: new ( ) ;
191
- for type_arg in type_arg_list. type_args ( ) {
192
- let type_arg: ast:: TypeArg = type_arg;
193
- result. push ( type_arg. type_ref ( ) ?) ;
194
- }
195
- Some ( result)
196
- }
197
-
198
- // FIXME: This should be a general utility (not even just for assists)
199
- fn substitute_type_params < N : AstNode > (
200
- db : & impl HirDatabase ,
201
- node : hir:: InFile < N > ,
202
- substs : & HashMap < hir:: TypeParam , ast:: TypeRef > ,
203
- ) -> N {
204
- let type_param_replacements = node
205
- . value
206
- . syntax ( )
207
- . descendants ( )
208
- . filter_map ( ast:: TypeRef :: cast)
209
- . filter_map ( |n| {
210
- let path = match & n {
211
- ast:: TypeRef :: PathType ( path_type) => path_type. path ( ) ?,
212
- _ => return None ,
213
- } ;
214
- let analyzer = hir:: SourceAnalyzer :: new ( db, node. with_value ( n. syntax ( ) ) , None ) ;
215
- let resolution = analyzer. resolve_path ( db, & path) ?;
216
- match resolution {
217
- hir:: PathResolution :: TypeParam ( tp) => Some ( ( n, substs. get ( & tp) ?. clone ( ) ) ) ,
218
- _ => None ,
219
- }
220
- } )
221
- . collect :: < Vec < _ > > ( ) ;
222
-
223
- if type_param_replacements. is_empty ( ) {
224
- node. value
225
- } else {
226
- edit:: replace_descendants ( & node. value , type_param_replacements. into_iter ( ) )
227
- }
228
- }
229
-
230
179
/// Given an `ast::ImplBlock`, resolves the target trait (the one being
231
180
/// implemented) to a `ast::TraitDef`.
232
181
fn resolve_target_trait_def (
@@ -400,6 +349,174 @@ impl Foo for S {
400
349
)
401
350
}
402
351
352
+ #[ test]
353
+ fn test_qualify_path_1 ( ) {
354
+ check_assist (
355
+ add_missing_impl_members,
356
+ "
357
+ mod foo {
358
+ pub struct Bar;
359
+ trait Foo { fn foo(&self, bar: Bar); }
360
+ }
361
+ struct S;
362
+ impl foo::Foo for S { <|> }" ,
363
+ "
364
+ mod foo {
365
+ pub struct Bar;
366
+ trait Foo { fn foo(&self, bar: Bar); }
367
+ }
368
+ struct S;
369
+ impl foo::Foo for S {
370
+ <|>fn foo(&self, bar: foo::Bar) { unimplemented!() }
371
+ }" ,
372
+ ) ;
373
+ }
374
+
375
+ #[ test]
376
+ fn test_qualify_path_generic ( ) {
377
+ check_assist (
378
+ add_missing_impl_members,
379
+ "
380
+ mod foo {
381
+ pub struct Bar<T>;
382
+ trait Foo { fn foo(&self, bar: Bar<u32>); }
383
+ }
384
+ struct S;
385
+ impl foo::Foo for S { <|> }" ,
386
+ "
387
+ mod foo {
388
+ pub struct Bar<T>;
389
+ trait Foo { fn foo(&self, bar: Bar<u32>); }
390
+ }
391
+ struct S;
392
+ impl foo::Foo for S {
393
+ <|>fn foo(&self, bar: foo::Bar<u32>) { unimplemented!() }
394
+ }" ,
395
+ ) ;
396
+ }
397
+
398
+ #[ test]
399
+ fn test_qualify_path_and_substitute_param ( ) {
400
+ check_assist (
401
+ add_missing_impl_members,
402
+ "
403
+ mod foo {
404
+ pub struct Bar<T>;
405
+ trait Foo<T> { fn foo(&self, bar: Bar<T>); }
406
+ }
407
+ struct S;
408
+ impl foo::Foo<u32> for S { <|> }" ,
409
+ "
410
+ mod foo {
411
+ pub struct Bar<T>;
412
+ trait Foo<T> { fn foo(&self, bar: Bar<T>); }
413
+ }
414
+ struct S;
415
+ impl foo::Foo<u32> for S {
416
+ <|>fn foo(&self, bar: foo::Bar<u32>) { unimplemented!() }
417
+ }" ,
418
+ ) ;
419
+ }
420
+
421
+ #[ test]
422
+ fn test_substitute_param_no_qualify ( ) {
423
+ // when substituting params, the substituted param should not be qualified!
424
+ check_assist (
425
+ add_missing_impl_members,
426
+ "
427
+ mod foo {
428
+ trait Foo<T> { fn foo(&self, bar: T); }
429
+ pub struct Param;
430
+ }
431
+ struct Param;
432
+ struct S;
433
+ impl foo::Foo<Param> for S { <|> }" ,
434
+ "
435
+ mod foo {
436
+ trait Foo<T> { fn foo(&self, bar: T); }
437
+ pub struct Param;
438
+ }
439
+ struct Param;
440
+ struct S;
441
+ impl foo::Foo<Param> for S {
442
+ <|>fn foo(&self, bar: Param) { unimplemented!() }
443
+ }" ,
444
+ ) ;
445
+ }
446
+
447
+ #[ test]
448
+ fn test_qualify_path_associated_item ( ) {
449
+ check_assist (
450
+ add_missing_impl_members,
451
+ "
452
+ mod foo {
453
+ pub struct Bar<T>;
454
+ impl Bar<T> { type Assoc = u32; }
455
+ trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
456
+ }
457
+ struct S;
458
+ impl foo::Foo for S { <|> }" ,
459
+ "
460
+ mod foo {
461
+ pub struct Bar<T>;
462
+ impl Bar<T> { type Assoc = u32; }
463
+ trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
464
+ }
465
+ struct S;
466
+ impl foo::Foo for S {
467
+ <|>fn foo(&self, bar: foo::Bar<u32>::Assoc) { unimplemented!() }
468
+ }" ,
469
+ ) ;
470
+ }
471
+
472
+ #[ test]
473
+ fn test_qualify_path_nested ( ) {
474
+ check_assist (
475
+ add_missing_impl_members,
476
+ "
477
+ mod foo {
478
+ pub struct Bar<T>;
479
+ pub struct Baz;
480
+ trait Foo { fn foo(&self, bar: Bar<Baz>); }
481
+ }
482
+ struct S;
483
+ impl foo::Foo for S { <|> }" ,
484
+ "
485
+ mod foo {
486
+ pub struct Bar<T>;
487
+ pub struct Baz;
488
+ trait Foo { fn foo(&self, bar: Bar<Baz>); }
489
+ }
490
+ struct S;
491
+ impl foo::Foo for S {
492
+ <|>fn foo(&self, bar: foo::Bar<foo::Baz>) { unimplemented!() }
493
+ }" ,
494
+ ) ;
495
+ }
496
+
497
+ #[ test]
498
+ fn test_qualify_path_fn_trait_notation ( ) {
499
+ check_assist (
500
+ add_missing_impl_members,
501
+ "
502
+ mod foo {
503
+ pub trait Fn<Args> { type Output; }
504
+ trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
505
+ }
506
+ struct S;
507
+ impl foo::Foo for S { <|> }" ,
508
+ "
509
+ mod foo {
510
+ pub trait Fn<Args> { type Output; }
511
+ trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
512
+ }
513
+ struct S;
514
+ impl foo::Foo for S {
515
+ <|>fn foo(&self, bar: dyn Fn(u32) -> i32) { unimplemented!() }
516
+ }" ,
517
+ ) ;
518
+ }
519
+
403
520
#[ test]
404
521
fn test_empty_trait ( ) {
405
522
check_assist_not_applicable (
0 commit comments