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, 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}