@@ -43,16 +43,12 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
4343 return None ;
4444 }
4545
46- let target_module = if let Some ( qualifier) = path. qualifier ( ) {
47- if let Some ( hir:: PathResolution :: Def ( hir:: ModuleDef :: Module ( module) ) ) =
48- ctx. sema . resolve_path ( & qualifier)
49- {
50- Some ( module. definition_source ( ctx. sema . db ) )
51- } else {
52- return None ;
53- }
54- } else {
55- None
46+ let target_module = match path. qualifier ( ) {
47+ Some ( qualifier) => match ctx. sema . resolve_path ( & qualifier) {
48+ Some ( hir:: PathResolution :: Def ( hir:: ModuleDef :: Module ( module) ) ) => Some ( module) ,
49+ _ => return None ,
50+ } ,
51+ None => None ,
5652 } ;
5753
5854 let function_builder = FunctionBuilder :: from_call ( & ctx, & call, & path, target_module) ?;
@@ -83,25 +79,29 @@ struct FunctionBuilder {
8379}
8480
8581impl FunctionBuilder {
86- /// Prepares a generated function that matches `call` in `generate_in`
87- /// (or as close to `call` as possible, if `generate_in` is `None`)
82+ /// Prepares a generated function that matches `call`.
83+ /// The function is generated in `target_module` or next to `call`
8884 fn from_call (
8985 ctx : & AssistContext ,
9086 call : & ast:: CallExpr ,
9187 path : & ast:: Path ,
92- target_module : Option < hir:: InFile < hir :: ModuleSource > > ,
88+ target_module : Option < hir:: Module > ,
9389 ) -> Option < Self > {
94- let needs_pub = target_module. is_some ( ) ;
9590 let mut file = ctx. frange . file_id ;
96- let target = if let Some ( target_module) = target_module {
97- let ( in_file, target) = next_space_for_fn_in_module ( ctx. sema . db , target_module) ?;
98- file = in_file;
99- target
100- } else {
101- next_space_for_fn_after_call_site ( & call) ?
91+ let target = match & target_module {
92+ Some ( target_module) => {
93+ let module_source = target_module. definition_source ( ctx. db ) ;
94+ let ( in_file, target) = next_space_for_fn_in_module ( ctx. sema . db , & module_source) ?;
95+ file = in_file;
96+ target
97+ }
98+ None => next_space_for_fn_after_call_site ( & call) ?,
10299 } ;
100+ let needs_pub = target_module. is_some ( ) ;
101+ let target_module = target_module. or_else ( || ctx. sema . scope ( target. syntax ( ) ) . module ( ) ) ?;
103102 let fn_name = fn_name ( & path) ?;
104- let ( type_params, params) = fn_args ( ctx, & call) ?;
103+ let ( type_params, params) = fn_args ( ctx, target_module, & call) ?;
104+
105105 Some ( Self { target, fn_name, type_params, params, file, needs_pub } )
106106 }
107107
@@ -144,6 +144,15 @@ enum GeneratedFunctionTarget {
144144 InEmptyItemList ( ast:: ItemList ) ,
145145}
146146
147+ impl GeneratedFunctionTarget {
148+ fn syntax ( & self ) -> & SyntaxNode {
149+ match self {
150+ GeneratedFunctionTarget :: BehindItem ( it) => it,
151+ GeneratedFunctionTarget :: InEmptyItemList ( it) => it. syntax ( ) ,
152+ }
153+ }
154+ }
155+
147156fn fn_name ( call : & ast:: Path ) -> Option < ast:: Name > {
148157 let name = call. segment ( ) ?. syntax ( ) . to_string ( ) ;
149158 Some ( ast:: make:: name ( & name) )
@@ -152,17 +161,17 @@ fn fn_name(call: &ast::Path) -> Option<ast::Name> {
152161/// Computes the type variables and arguments required for the generated function
153162fn fn_args (
154163 ctx : & AssistContext ,
164+ target_module : hir:: Module ,
155165 call : & ast:: CallExpr ,
156166) -> Option < ( Option < ast:: TypeParamList > , ast:: ParamList ) > {
157167 let mut arg_names = Vec :: new ( ) ;
158168 let mut arg_types = Vec :: new ( ) ;
159169 for arg in call. arg_list ( ) ?. args ( ) {
160- let arg_name = match fn_arg_name ( & arg) {
170+ arg_names . push ( match fn_arg_name ( & arg) {
161171 Some ( name) => name,
162172 None => String :: from ( "arg" ) ,
163- } ;
164- arg_names. push ( arg_name) ;
165- arg_types. push ( match fn_arg_type ( ctx, & arg) {
173+ } ) ;
174+ arg_types. push ( match fn_arg_type ( ctx, target_module, & arg) {
166175 Some ( ty) => ty,
167176 None => String :: from ( "()" ) ,
168177 } ) ;
@@ -218,12 +227,21 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
218227 }
219228}
220229
221- fn fn_arg_type ( ctx : & AssistContext , fn_arg : & ast:: Expr ) -> Option < String > {
230+ fn fn_arg_type (
231+ ctx : & AssistContext ,
232+ target_module : hir:: Module ,
233+ fn_arg : & ast:: Expr ,
234+ ) -> Option < String > {
222235 let ty = ctx. sema . type_of_expr ( fn_arg) ?;
223236 if ty. is_unknown ( ) {
224237 return None ;
225238 }
226- Some ( ty. display ( ctx. sema . db ) . to_string ( ) )
239+
240+ if let Ok ( rendered) = ty. display_source_code ( ctx. db , target_module. into ( ) ) {
241+ Some ( rendered)
242+ } else {
243+ None
244+ }
227245}
228246
229247/// Returns the position inside the current mod or file
@@ -252,10 +270,10 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu
252270
253271fn next_space_for_fn_in_module (
254272 db : & dyn hir:: db:: AstDatabase ,
255- module : hir:: InFile < hir:: ModuleSource > ,
273+ module_source : & hir:: InFile < hir:: ModuleSource > ,
256274) -> Option < ( FileId , GeneratedFunctionTarget ) > {
257- let file = module . file_id . original_file ( db) ;
258- let assist_item = match module . value {
275+ let file = module_source . file_id . original_file ( db) ;
276+ let assist_item = match & module_source . value {
259277 hir:: ModuleSource :: SourceFile ( it) => {
260278 if let Some ( last_item) = it. items ( ) . last ( ) {
261279 GeneratedFunctionTarget :: BehindItem ( last_item. syntax ( ) . clone ( ) )
@@ -599,8 +617,33 @@ fn bar(foo: impl Foo) {
599617 }
600618
601619 #[ test]
602- #[ ignore]
603- // FIXME print paths properly to make this test pass
620+ fn borrowed_arg ( ) {
621+ check_assist (
622+ add_function,
623+ r"
624+ struct Baz;
625+ fn baz() -> Baz { todo!() }
626+
627+ fn foo() {
628+ bar<|>(&baz())
629+ }
630+ " ,
631+ r"
632+ struct Baz;
633+ fn baz() -> Baz { todo!() }
634+
635+ fn foo() {
636+ bar(&baz())
637+ }
638+
639+ fn bar(baz: &Baz) {
640+ <|>todo!()
641+ }
642+ " ,
643+ )
644+ }
645+
646+ #[ test]
604647 fn add_function_with_qualified_path_arg ( ) {
605648 check_assist (
606649 add_function,
@@ -609,25 +652,21 @@ mod Baz {
609652 pub struct Bof;
610653 pub fn baz() -> Bof { Bof }
611654}
612- mod Foo {
613- fn foo() {
614- <|>bar(super::Baz::baz())
615- }
655+ fn foo() {
656+ <|>bar(Baz::baz())
616657}
617658" ,
618659 r"
619660mod Baz {
620661 pub struct Bof;
621662 pub fn baz() -> Bof { Bof }
622663}
623- mod Foo {
624- fn foo() {
625- bar(super::Baz::baz())
626- }
664+ fn foo() {
665+ bar(Baz::baz())
666+ }
627667
628- fn bar(baz: super::Baz::Bof) {
629- <|>todo!()
630- }
668+ fn bar(baz: Baz::Bof) {
669+ <|>todo!()
631670}
632671" ,
633672 )
@@ -808,6 +847,40 @@ fn foo() {
808847 )
809848 }
810849
850+ #[ test]
851+ #[ ignore]
852+ // Ignored until local imports are supported.
853+ // See https://github.com/rust-analyzer/rust-analyzer/issues/1165
854+ fn qualified_path_uses_correct_scope ( ) {
855+ check_assist (
856+ add_function,
857+ "
858+ mod foo {
859+ pub struct Foo;
860+ }
861+ fn bar() {
862+ use foo::Foo;
863+ let foo = Foo;
864+ baz<|>(foo)
865+ }
866+ " ,
867+ "
868+ mod foo {
869+ pub struct Foo;
870+ }
871+ fn bar() {
872+ use foo::Foo;
873+ let foo = Foo;
874+ baz(foo)
875+ }
876+
877+ fn baz(foo: foo::Foo) {
878+ <|>todo!()
879+ }
880+ " ,
881+ )
882+ }
883+
811884 #[ test]
812885 fn add_function_in_module_containing_other_items ( ) {
813886 check_assist (
@@ -919,21 +992,6 @@ fn bar(baz: ()) {}
919992 )
920993 }
921994
922- #[ test]
923- fn add_function_not_applicable_if_function_path_not_singleton ( ) {
924- // In the future this assist could be extended to generate functions
925- // if the path is in the same crate (or even the same workspace).
926- // For the beginning, I think this is fine.
927- check_assist_not_applicable (
928- add_function,
929- r"
930- fn foo() {
931- other_crate::bar<|>();
932- }
933- " ,
934- )
935- }
936-
937995 #[ test]
938996 #[ ignore]
939997 fn create_method_with_no_args ( ) {
0 commit comments