Skip to main content

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