| use hir::{ |
| db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, ImportPathConfig, ItemInNs, |
| ModuleDef, |
| }; |
| use ide_db::assists::{AssistId, AssistKind}; |
| use syntax::{ast, AstNode}; |
| |
| use crate::{ |
| assist_context::{AssistContext, Assists}, |
| handlers::qualify_path::QualifyCandidate, |
| }; |
| |
| // Assist: qualify_method_call |
| // |
| // Replaces the method call with a qualified function call. |
| // |
| // ``` |
| // struct Foo; |
| // impl Foo { |
| // fn foo(&self) {} |
| // } |
| // fn main() { |
| // let foo = Foo; |
| // foo.fo$0o(); |
| // } |
| // ``` |
| // -> |
| // ``` |
| // struct Foo; |
| // impl Foo { |
| // fn foo(&self) {} |
| // } |
| // fn main() { |
| // let foo = Foo; |
| // Foo::foo(&foo); |
| // } |
| // ``` |
| pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { |
| let name: ast::NameRef = ctx.find_node_at_offset()?; |
| let call = name.syntax().parent().and_then(ast::MethodCallExpr::cast)?; |
| |
| let ident = name.ident_token()?; |
| |
| let range = call.syntax().text_range(); |
| let resolved_call = ctx.sema.resolve_method_call(&call)?; |
| |
| let current_module = ctx.sema.scope(call.syntax())?.module(); |
| let target_module_def = ModuleDef::from(resolved_call); |
| let item_in_ns = ItemInNs::from(target_module_def); |
| let receiver_path = current_module.find_path( |
| ctx.sema.db, |
| item_for_path_search(ctx.sema.db, item_in_ns)?, |
| ImportPathConfig { |
| prefer_no_std: ctx.config.prefer_no_std, |
| prefer_prelude: ctx.config.prefer_prelude, |
| }, |
| )?; |
| |
| let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call); |
| |
| acc.add( |
| AssistId("qualify_method_call", AssistKind::RefactorRewrite), |
| format!("Qualify `{ident}` method call"), |
| range, |
| |builder| { |
| qualify_candidate.qualify( |
| |replace_with: String| builder.replace(range, replace_with), |
| &receiver_path, |
| item_in_ns, |
| ) |
| }, |
| ); |
| Some(()) |
| } |
| |
| fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option<ItemInNs> { |
| Some(match item { |
| ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) { |
| Some(assoc_item) => match assoc_item.container(db) { |
| AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)), |
| AssocItemContainer::Impl(impl_) => match impl_.trait_(db) { |
| None => ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)), |
| Some(trait_) => ItemInNs::from(ModuleDef::from(trait_)), |
| }, |
| }, |
| None => item, |
| }, |
| ItemInNs::Macros(_) => item, |
| }) |
| } |
| |
| fn item_as_assoc(db: &dyn HirDatabase, item: ItemInNs) -> Option<AssocItem> { |
| item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::tests::{check_assist, check_assist_not_applicable}; |
| |
| #[test] |
| fn struct_method() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(&self) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| foo.fo$0o() |
| } |
| "#, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(&self) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| Foo::foo(&foo) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn struct_method_multi_params() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(&self, p1: i32, p2: u32) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| foo.fo$0o(9, 9u) |
| } |
| "#, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(&self, p1: i32, p2: u32) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| Foo::foo(&foo, 9, 9u) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn struct_method_consume() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(self, p1: i32, p2: u32) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| foo.fo$0o(9, 9u) |
| } |
| "#, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(self, p1: i32, p2: u32) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| Foo::foo(foo, 9, 9u) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn struct_method_exclusive() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(&mut self, p1: i32, p2: u32) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| foo.fo$0o(9, 9u) |
| } |
| "#, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(&mut self, p1: i32, p2: u32) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| Foo::foo(&mut foo, 9, 9u) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn struct_method_cross_crate() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| //- /main.rs crate:main deps:dep |
| fn main() { |
| let foo = dep::test_mod::Foo {}; |
| foo.fo$0o(9, 9u) |
| } |
| //- /dep.rs crate:dep |
| pub mod test_mod { |
| pub struct Foo; |
| impl Foo { |
| pub fn foo(&mut self, p1: i32, p2: u32) {} |
| } |
| } |
| "#, |
| r#" |
| fn main() { |
| let foo = dep::test_mod::Foo {}; |
| dep::test_mod::Foo::foo(&mut foo, 9, 9u) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn struct_method_generic() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo<T>(&self) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| foo.fo$0o::<()>() |
| } |
| "#, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo<T>(&self) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| Foo::foo::<()>(&foo) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn trait_method() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method(&self); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method(&self) {} |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = test_mod::TestStruct {}; |
| test_struct.test_meth$0od() |
| } |
| "#, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method(&self); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method(&self) {} |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = test_mod::TestStruct {}; |
| TestTrait::test_method(&test_struct) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn trait_method_multi_params() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method(&self, p1: i32, p2: u32); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method(&self, p1: i32, p2: u32) {} |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = test_mod::TestStruct {}; |
| test_struct.test_meth$0od(12, 32u) |
| } |
| "#, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method(&self, p1: i32, p2: u32); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method(&self, p1: i32, p2: u32) {} |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = test_mod::TestStruct {}; |
| TestTrait::test_method(&test_struct, 12, 32u) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn trait_method_consume() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method(self, p1: i32, p2: u32); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method(self, p1: i32, p2: u32) {} |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = test_mod::TestStruct {}; |
| test_struct.test_meth$0od(12, 32u) |
| } |
| "#, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method(self, p1: i32, p2: u32); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method(self, p1: i32, p2: u32) {} |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = test_mod::TestStruct {}; |
| TestTrait::test_method(test_struct, 12, 32u) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn trait_method_exclusive() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method(&mut self, p1: i32, p2: u32); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method(&mut self, p1: i32, p2: u32); |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = test_mod::TestStruct {}; |
| test_struct.test_meth$0od(12, 32u) |
| } |
| "#, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method(&mut self, p1: i32, p2: u32); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method(&mut self, p1: i32, p2: u32); |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = test_mod::TestStruct {}; |
| TestTrait::test_method(&mut test_struct, 12, 32u) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn trait_method_cross_crate() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| //- /main.rs crate:main deps:dep |
| fn main() { |
| let foo = dep::test_mod::Foo {}; |
| foo.fo$0o(9, 9u) |
| } |
| //- /dep.rs crate:dep |
| pub mod test_mod { |
| pub struct Foo; |
| impl Foo { |
| pub fn foo(&mut self, p1: i32, p2: u32) {} |
| } |
| } |
| "#, |
| r#" |
| fn main() { |
| let foo = dep::test_mod::Foo {}; |
| dep::test_mod::Foo::foo(&mut foo, 9, 9u) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn trait_method_generic() { |
| check_assist( |
| qualify_method_call, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method<T>(&self); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method<T>(&self) {} |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = TestStruct {}; |
| test_struct.test_meth$0od::<()>() |
| } |
| "#, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method<T>(&self); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method<T>(&self) {} |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = TestStruct {}; |
| TestTrait::test_method::<()>(&test_struct) |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn struct_method_over_struct_instance() { |
| check_assist_not_applicable( |
| qualify_method_call, |
| r#" |
| struct Foo; |
| impl Foo { |
| fn foo(&self) {} |
| } |
| |
| fn main() { |
| let foo = Foo {}; |
| f$0oo.foo() |
| } |
| "#, |
| ); |
| } |
| |
| #[test] |
| fn trait_method_over_struct_instance() { |
| check_assist_not_applicable( |
| qualify_method_call, |
| r#" |
| mod test_mod { |
| pub trait TestTrait { |
| fn test_method(&self); |
| } |
| pub struct TestStruct {} |
| impl TestTrait for TestStruct { |
| fn test_method(&self) {} |
| } |
| } |
| |
| use test_mod::*; |
| |
| fn main() { |
| let test_struct = test_mod::TestStruct {}; |
| tes$0t_struct.test_method() |
| } |
| "#, |
| ); |
| } |
| } |