1- use std:: collections:: HashMap ;
2-
3- use hir:: { db:: HirDatabase , HasSource } ;
1+ use hir:: { db:: HirDatabase , HasSource , InFile } ;
42use ra_syntax:: {
53 ast:: { self , edit, make, AstNode , NameOwner } ,
64 SmolStr ,
75} ;
86
9- use crate :: { Assist , AssistCtx , AssistId } ;
7+ use crate :: {
8+ ast_transform:: { self , AstTransform , QualifyPaths , SubstituteTypeParams } ,
9+ Assist , AssistCtx , AssistId ,
10+ } ;
1011
1112#[ derive( PartialEq ) ]
1213enum AddMissingImplMembersMode {
@@ -134,25 +135,23 @@ fn add_missing_impl_members_inner(
134135 return None ;
135136 }
136137
137- let file_id = ctx. frange . file_id ;
138138 let db = ctx. db ;
139+ let file_id = ctx. frange . file_id ;
140+ let trait_file_id = trait_. source ( db) . file_id ;
139141
140142 ctx. add_assist ( AssistId ( assist_id) , label, |edit| {
141143 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) ) ;
151152 let items = missing_items
152153 . 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) ) )
156155 . map ( |it| match it {
157156 ast:: ImplItem :: FnDef ( def) => ast:: ImplItem :: FnDef ( add_body ( def) ) ,
158157 _ => it,
@@ -177,56 +176,6 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
177176 }
178177}
179178
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-
230179/// Given an `ast::ImplBlock`, resolves the target trait (the one being
231180/// implemented) to a `ast::TraitDef`.
232181fn resolve_target_trait_def (
@@ -400,6 +349,174 @@ impl Foo for S {
400349 )
401350 }
402351
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+
403520 #[ test]
404521 fn test_empty_trait ( ) {
405522 check_assist_not_applicable (
0 commit comments