ide_assists/handlers/
qualify_method_call.rs

1use hir::{AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef, db::HirDatabase};
2use ide_db::assists::AssistId;
3use syntax::{AstNode, ast};
4
5use crate::{
6    assist_context::{AssistContext, Assists},
7    handlers::qualify_path::QualifyCandidate,
8};
9
10// Assist: qualify_method_call
11//
12// Replaces the method call with a qualified function call.
13//
14// ```
15// struct Foo;
16// impl Foo {
17//     fn foo(&self) {}
18// }
19// fn main() {
20//     let foo = Foo;
21//     foo.fo$0o();
22// }
23// ```
24// ->
25// ```
26// struct Foo;
27// impl Foo {
28//     fn foo(&self) {}
29// }
30// fn main() {
31//     let foo = Foo;
32//     Foo::foo(&foo);
33// }
34// ```
35pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
36    let name: ast::NameRef = ctx.find_node_at_offset()?;
37    let call = name.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
38
39    let ident = name.ident_token()?;
40
41    let range = call.syntax().text_range();
42    let resolved_call = ctx.sema.resolve_method_call(&call)?;
43
44    let current_module = ctx.sema.scope(call.syntax())?.module();
45    let current_edition = current_module.krate(ctx.db()).edition(ctx.db());
46    let target_module_def = ModuleDef::from(resolved_call);
47    let item_in_ns = ItemInNs::from(target_module_def);
48    let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(current_module.krate(ctx.sema.db)));
49    let receiver_path = current_module.find_path(
50        ctx.sema.db,
51        item_for_path_search(ctx.sema.db, item_in_ns)?,
52        cfg,
53    )?;
54
55    let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call);
56
57    acc.add(
58        AssistId::refactor_rewrite("qualify_method_call"),
59        format!("Qualify `{ident}` method call"),
60        range,
61        |builder| {
62            qualify_candidate.qualify(
63                |replace_with: String| builder.replace(range, replace_with),
64                &receiver_path,
65                item_in_ns,
66                current_edition,
67            )
68        },
69    );
70    Some(())
71}
72
73fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option<ItemInNs> {
74    Some(match item {
75        ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
76            Some(assoc_item) => match assoc_item.container(db) {
77                AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
78                AssocItemContainer::Impl(impl_) => match impl_.trait_(db) {
79                    None => ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)),
80                    Some(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
81                },
82            },
83            None => item,
84        },
85        ItemInNs::Macros(_) => item,
86    })
87}
88
89fn item_as_assoc(db: &dyn HirDatabase, item: ItemInNs) -> Option<AssocItem> {
90    item.into_module_def().as_assoc_item(db)
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::tests::{check_assist, check_assist_not_applicable};
97
98    #[test]
99    fn struct_method() {
100        check_assist(
101            qualify_method_call,
102            r#"
103struct Foo;
104impl Foo {
105    fn foo(&self) {}
106}
107
108fn main() {
109    let foo = Foo {};
110    foo.fo$0o()
111}
112"#,
113            r#"
114struct Foo;
115impl Foo {
116    fn foo(&self) {}
117}
118
119fn main() {
120    let foo = Foo {};
121    Foo::foo(&foo)
122}
123"#,
124        );
125    }
126
127    #[test]
128    fn struct_method_multi_params() {
129        check_assist(
130            qualify_method_call,
131            r#"
132struct Foo;
133impl Foo {
134    fn foo(&self, p1: i32, p2: u32) {}
135}
136
137fn main() {
138    let foo = Foo {};
139    foo.fo$0o(9, 9u)
140}
141"#,
142            r#"
143struct Foo;
144impl Foo {
145    fn foo(&self, p1: i32, p2: u32) {}
146}
147
148fn main() {
149    let foo = Foo {};
150    Foo::foo(&foo, 9, 9u)
151}
152"#,
153        );
154    }
155
156    #[test]
157    fn struct_method_consume() {
158        check_assist(
159            qualify_method_call,
160            r#"
161struct Foo;
162impl Foo {
163    fn foo(self, p1: i32, p2: u32) {}
164}
165
166fn main() {
167    let foo = Foo {};
168    foo.fo$0o(9, 9u)
169}
170"#,
171            r#"
172struct Foo;
173impl Foo {
174    fn foo(self, p1: i32, p2: u32) {}
175}
176
177fn main() {
178    let foo = Foo {};
179    Foo::foo(foo, 9, 9u)
180}
181"#,
182        );
183    }
184
185    #[test]
186    fn struct_method_exclusive() {
187        check_assist(
188            qualify_method_call,
189            r#"
190struct Foo;
191impl Foo {
192    fn foo(&mut self, p1: i32, p2: u32) {}
193}
194
195fn main() {
196    let foo = Foo {};
197    foo.fo$0o(9, 9u)
198}
199"#,
200            r#"
201struct Foo;
202impl Foo {
203    fn foo(&mut self, p1: i32, p2: u32) {}
204}
205
206fn main() {
207    let foo = Foo {};
208    Foo::foo(&mut foo, 9, 9u)
209}
210"#,
211        );
212    }
213
214    #[test]
215    fn struct_method_cross_crate() {
216        check_assist(
217            qualify_method_call,
218            r#"
219//- /main.rs crate:main deps:dep
220fn main() {
221    let foo = dep::test_mod::Foo {};
222    foo.fo$0o(9, 9u)
223}
224//- /dep.rs crate:dep
225pub mod test_mod {
226    pub struct Foo;
227    impl Foo {
228        pub fn foo(&mut self, p1: i32, p2: u32) {}
229    }
230}
231"#,
232            r#"
233fn main() {
234    let foo = dep::test_mod::Foo {};
235    dep::test_mod::Foo::foo(&mut foo, 9, 9u)
236}
237"#,
238        );
239    }
240
241    #[test]
242    fn struct_method_generic() {
243        check_assist(
244            qualify_method_call,
245            r#"
246struct Foo;
247impl Foo {
248    fn foo<T>(&self) {}
249}
250
251fn main() {
252    let foo = Foo {};
253    foo.fo$0o::<()>()
254}
255"#,
256            r#"
257struct Foo;
258impl Foo {
259    fn foo<T>(&self) {}
260}
261
262fn main() {
263    let foo = Foo {};
264    Foo::foo::<()>(&foo)
265}
266"#,
267        );
268    }
269
270    #[test]
271    fn trait_method() {
272        check_assist(
273            qualify_method_call,
274            r#"
275mod test_mod {
276    pub trait TestTrait {
277        fn test_method(&self);
278    }
279    pub struct TestStruct {}
280    impl TestTrait for TestStruct {
281        fn test_method(&self) {}
282    }
283}
284
285use test_mod::*;
286
287fn main() {
288    let test_struct = test_mod::TestStruct {};
289    test_struct.test_meth$0od()
290}
291"#,
292            r#"
293mod test_mod {
294    pub trait TestTrait {
295        fn test_method(&self);
296    }
297    pub struct TestStruct {}
298    impl TestTrait for TestStruct {
299        fn test_method(&self) {}
300    }
301}
302
303use test_mod::*;
304
305fn main() {
306    let test_struct = test_mod::TestStruct {};
307    TestTrait::test_method(&test_struct)
308}
309"#,
310        );
311    }
312
313    #[test]
314    fn trait_method_multi_params() {
315        check_assist(
316            qualify_method_call,
317            r#"
318mod test_mod {
319    pub trait TestTrait {
320        fn test_method(&self, p1: i32, p2: u32);
321    }
322    pub struct TestStruct {}
323    impl TestTrait for TestStruct {
324        fn test_method(&self, p1: i32, p2: u32) {}
325    }
326}
327
328use test_mod::*;
329
330fn main() {
331    let test_struct = test_mod::TestStruct {};
332    test_struct.test_meth$0od(12, 32u)
333}
334"#,
335            r#"
336mod test_mod {
337    pub trait TestTrait {
338        fn test_method(&self, p1: i32, p2: u32);
339    }
340    pub struct TestStruct {}
341    impl TestTrait for TestStruct {
342        fn test_method(&self, p1: i32, p2: u32) {}
343    }
344}
345
346use test_mod::*;
347
348fn main() {
349    let test_struct = test_mod::TestStruct {};
350    TestTrait::test_method(&test_struct, 12, 32u)
351}
352"#,
353        );
354    }
355
356    #[test]
357    fn trait_method_consume() {
358        check_assist(
359            qualify_method_call,
360            r#"
361mod test_mod {
362    pub trait TestTrait {
363        fn test_method(self, p1: i32, p2: u32);
364    }
365    pub struct TestStruct {}
366    impl TestTrait for TestStruct {
367        fn test_method(self, p1: i32, p2: u32) {}
368    }
369}
370
371use test_mod::*;
372
373fn main() {
374    let test_struct = test_mod::TestStruct {};
375    test_struct.test_meth$0od(12, 32u)
376}
377"#,
378            r#"
379mod test_mod {
380    pub trait TestTrait {
381        fn test_method(self, p1: i32, p2: u32);
382    }
383    pub struct TestStruct {}
384    impl TestTrait for TestStruct {
385        fn test_method(self, p1: i32, p2: u32) {}
386    }
387}
388
389use test_mod::*;
390
391fn main() {
392    let test_struct = test_mod::TestStruct {};
393    TestTrait::test_method(test_struct, 12, 32u)
394}
395"#,
396        );
397    }
398
399    #[test]
400    fn trait_method_exclusive() {
401        check_assist(
402            qualify_method_call,
403            r#"
404mod test_mod {
405    pub trait TestTrait {
406        fn test_method(&mut self, p1: i32, p2: u32);
407    }
408    pub struct TestStruct {}
409    impl TestTrait for TestStruct {
410        fn test_method(&mut self, p1: i32, p2: u32);
411    }
412}
413
414use test_mod::*;
415
416fn main() {
417    let test_struct = test_mod::TestStruct {};
418    test_struct.test_meth$0od(12, 32u)
419}
420"#,
421            r#"
422mod test_mod {
423    pub trait TestTrait {
424        fn test_method(&mut self, p1: i32, p2: u32);
425    }
426    pub struct TestStruct {}
427    impl TestTrait for TestStruct {
428        fn test_method(&mut self, p1: i32, p2: u32);
429    }
430}
431
432use test_mod::*;
433
434fn main() {
435    let test_struct = test_mod::TestStruct {};
436    TestTrait::test_method(&mut test_struct, 12, 32u)
437}
438"#,
439        );
440    }
441
442    #[test]
443    fn trait_method_cross_crate() {
444        check_assist(
445            qualify_method_call,
446            r#"
447//- /main.rs crate:main deps:dep
448fn main() {
449    let foo = dep::test_mod::Foo {};
450    foo.fo$0o(9, 9u)
451}
452//- /dep.rs crate:dep
453pub mod test_mod {
454    pub struct Foo;
455    impl Foo {
456        pub fn foo(&mut self, p1: i32, p2: u32) {}
457    }
458}
459"#,
460            r#"
461fn main() {
462    let foo = dep::test_mod::Foo {};
463    dep::test_mod::Foo::foo(&mut foo, 9, 9u)
464}
465"#,
466        );
467    }
468
469    #[test]
470    fn trait_method_generic() {
471        check_assist(
472            qualify_method_call,
473            r#"
474mod test_mod {
475    pub trait TestTrait {
476        fn test_method<T>(&self);
477    }
478    pub struct TestStruct {}
479    impl TestTrait for TestStruct {
480        fn test_method<T>(&self) {}
481    }
482}
483
484use test_mod::*;
485
486fn main() {
487    let test_struct = TestStruct {};
488    test_struct.test_meth$0od::<()>()
489}
490"#,
491            r#"
492mod test_mod {
493    pub trait TestTrait {
494        fn test_method<T>(&self);
495    }
496    pub struct TestStruct {}
497    impl TestTrait for TestStruct {
498        fn test_method<T>(&self) {}
499    }
500}
501
502use test_mod::*;
503
504fn main() {
505    let test_struct = TestStruct {};
506    TestTrait::test_method::<()>(&test_struct)
507}
508"#,
509        );
510    }
511
512    #[test]
513    fn struct_method_over_struct_instance() {
514        check_assist_not_applicable(
515            qualify_method_call,
516            r#"
517struct Foo;
518impl Foo {
519    fn foo(&self) {}
520}
521
522fn main() {
523    let foo = Foo {};
524    f$0oo.foo()
525}
526"#,
527        );
528    }
529
530    #[test]
531    fn trait_method_over_struct_instance() {
532        check_assist_not_applicable(
533            qualify_method_call,
534            r#"
535mod test_mod {
536    pub trait TestTrait {
537        fn test_method(&self);
538    }
539    pub struct TestStruct {}
540    impl TestTrait for TestStruct {
541        fn test_method(&self) {}
542    }
543}
544
545use test_mod::*;
546
547fn main() {
548    let test_struct = test_mod::TestStruct {};
549    tes$0t_struct.test_method()
550}
551"#,
552        );
553    }
554}