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
10pub(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}