Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/qualify_method_call.rs')
| -rw-r--r-- | crates/ide-assists/src/handlers/qualify_method_call.rs | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs new file mode 100644 index 0000000000..ba0a142718 --- /dev/null +++ b/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -0,0 +1,531 @@ +use hir::{ItemInNs, ModuleDef}; +use ide_db::{ + assists::{AssistId, AssistKind}, + imports::import_assets::item_for_path_search, +}; +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_use_path(ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?)?; + + let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call); + + acc.add( + AssistId("qualify_method_call", AssistKind::RefactorInline), + format!("Qualify `{}` method call", ident.text()), + range, + |builder| { + qualify_candidate.qualify( + |replace_with: String| builder.replace(range, replace_with), + &receiver_path, + item_in_ns, + ) + }, + ); + Some(()) +} + +#[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_stuct_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_stuct_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() +} +"#, + ); + } +} |