From bb00b09d22d4cd892ce7f4a605a2c8fae5d0095c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Palenica?= Date: Wed, 20 Oct 2021 23:28:19 -0700 Subject: [PATCH 1/7] Add qualify method call assist --- .../src/handlers/qualify_method_call.rs | 481 ++++++++++++++++++ .../ide_assists/src/handlers/qualify_path.rs | 45 +- crates/ide_assists/src/lib.rs | 2 + crates/ide_db/src/helpers/import_assets.rs | 4 +- 4 files changed, 515 insertions(+), 17 deletions(-) create mode 100644 crates/ide_assists/src/handlers/qualify_method_call.rs 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..00b12411b0 --- /dev/null +++ b/crates/ide_assists/src/handlers/qualify_method_call.rs @@ -0,0 +1,481 @@ +use hir::{ItemInNs, ModuleDef}; +use ide_db::{assists::{AssistId, AssistKind}, helpers::import_assets::item_for_path_search}; +use syntax::{AstNode, ast}; + +use crate::{assist_context::{AssistContext, Assists}, handlers::qualify_path::QualifyCandidate}; + +// Assist: qualify_method_call +// +// If the name is resolvable, provides fully qualified path for it. +// +// ``` +// 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 call: ast::MethodCallExpr = ctx.find_node_at_offset()?; + let fn_name = &call.name_ref()?; + + // let callExpr = path_expr.syntax(); + 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 call `{}`", fn_name), + range, + |builder| { + qualify_candidate.qualify( + |replace_with: String| builder.replace(range, replace_with), + &receiver_path, + item_in_ns + ) + } + ); + Some(()) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist}; + use super::*; + + #[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(&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 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(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&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(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } +} + +use test_mod::*; + +fn main() { + let test_struct = TestStruct {}; + TestTrait::test_method::<()>(&test_struct) +} +"#, + ); + } +} + diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index 0b33acc39b..ecfced83b1 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs @@ -1,10 +1,7 @@ use std::iter; use hir::AsAssocItem; -use ide_db::helpers::{ - import_assets::{ImportCandidate, LocatedImport}, - mod_path_to_ast, -}; +use ide_db::helpers::{import_assets::{ImportCandidate, LocatedImport}, mod_path_to_ast}; use ide_db::RootDatabase; use syntax::{ ast, @@ -37,6 +34,7 @@ use crate::{ // ``` pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; + let mut proposed_imports = import_assets.search_for_relative_paths(&ctx.sema); if proposed_imports.is_empty() { return None; @@ -91,16 +89,17 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> } Some(()) } - -enum QualifyCandidate<'db> { +#[derive(Debug)] +pub(crate) enum QualifyCandidate<'db> { QualifierStart(ast::PathSegment, Option), UnqualifiedName(Option), TraitAssocItem(ast::Path, ast::PathSegment), TraitMethod(&'db RootDatabase, ast::MethodCallExpr), + ImplMethod(&'db RootDatabase, ast::MethodCallExpr, hir::Function), } impl QualifyCandidate<'_> { - fn qualify( + pub(crate) fn qualify( &self, mut replacer: impl FnMut(String), import: &hir::ModPath, @@ -122,24 +121,26 @@ impl QualifyCandidate<'_> { QualifyCandidate::TraitMethod(db, mcall_expr) => { Self::qualify_trait_method(db, mcall_expr, replacer, import, item); } + QualifyCandidate::ImplMethod(db, mcall_expr, hir_fn) => { + Self::qualify_fn_call(db, mcall_expr, replacer, import, hir_fn); + } } } - fn qualify_trait_method( + fn qualify_fn_call( db: &RootDatabase, mcall_expr: &ast::MethodCallExpr, mut replacer: impl FnMut(String), import: ast::Path, - item: hir::ItemInNs, + hir_fn: &hir::Function, ) -> Option<()> { let receiver = mcall_expr.receiver()?; - let trait_method_name = mcall_expr.name_ref()?; - let generics = + let method_name = mcall_expr.name_ref()?; + let generics = mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string); let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); - let trait_ = item_as_trait(db, item)?; - let method = find_trait_method(db, trait_, &trait_method_name)?; - if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) { + + if let Some(self_access) = hir_fn.self_param(db).map(|sp| sp.access(db)) { let receiver = match self_access { hir::Access::Shared => make::expr_ref(receiver, false), hir::Access::Exclusive => make::expr_ref(receiver, true), @@ -148,7 +149,7 @@ impl QualifyCandidate<'_> { replacer(format!( "{}::{}{}{}", import, - trait_method_name, + method_name, generics, match arg_list { Some(args) => make::arg_list(iter::once(receiver).chain(args)), @@ -158,6 +159,20 @@ impl QualifyCandidate<'_> { } Some(()) } + + fn qualify_trait_method( + db: &RootDatabase, + mcall_expr: &ast::MethodCallExpr, + replacer: impl FnMut(String), + import: ast::Path, + item: hir::ItemInNs, + ) -> Option<()> { + let trait_method_name = mcall_expr.name_ref()?; + let trait_ = item_as_trait(db, item)?; + let method = find_trait_method(db, trait_, &trait_method_name)?; + Self::qualify_fn_call(db, mcall_expr, replacer, import, &method); + Some(()) + } } fn find_trait_method( diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index dccd071dcc..2801c3c44b 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs @@ -160,6 +160,7 @@ mod handlers { mod promote_local_to_const; mod pull_assignment_up; mod qualify_path; + mod qualify_method_call; mod raw_string; mod remove_dbg; mod remove_mut; @@ -241,6 +242,7 @@ mod handlers { pull_assignment_up::pull_assignment_up, promote_local_to_const::promote_local_to_const, qualify_path::qualify_path, + qualify_method_call::qualify_method_call, raw_string::add_hash, raw_string::make_usual_string, raw_string::remove_hash, diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index d6bb19d129..7170e14d84 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -372,7 +372,7 @@ fn import_for_item( }) } -fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option { +pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option { Some(match item { ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) { Some(assoc_item) => match assoc_item.container(db) { @@ -619,6 +619,6 @@ fn path_import_candidate( }) } -fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option { +pub fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option { item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)) } From c8820d342f444f76795d926e1e745f44cca08dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Palenica?= Date: Wed, 20 Oct 2021 23:35:14 -0700 Subject: [PATCH 2/7] Run cargo fmt --- .../src/handlers/qualify_method_call.rs | 30 +++++++++++-------- .../ide_assists/src/handlers/qualify_path.rs | 7 +++-- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/crates/ide_assists/src/handlers/qualify_method_call.rs b/crates/ide_assists/src/handlers/qualify_method_call.rs index 00b12411b0..773e912e25 100644 --- a/crates/ide_assists/src/handlers/qualify_method_call.rs +++ b/crates/ide_assists/src/handlers/qualify_method_call.rs @@ -1,8 +1,14 @@ use hir::{ItemInNs, ModuleDef}; -use ide_db::{assists::{AssistId, AssistKind}, helpers::import_assets::item_for_path_search}; -use syntax::{AstNode, ast}; +use ide_db::{ + assists::{AssistId, AssistKind}, + helpers::import_assets::item_for_path_search, +}; +use syntax::{ast, AstNode}; -use crate::{assist_context::{AssistContext, Assists}, handlers::qualify_path::QualifyCandidate}; +use crate::{ + assist_context::{AssistContext, Assists}, + handlers::qualify_path::QualifyCandidate, +}; // Assist: qualify_method_call // @@ -11,7 +17,7 @@ use crate::{assist_context::{AssistContext, Assists}, handlers::qualify_path::Qu // ``` // struct Foo; // impl Foo { -// fn foo(&self) {} +// fn foo(&self) {} // } // fn main() { // let foo = Foo; @@ -22,7 +28,7 @@ use crate::{assist_context::{AssistContext, Assists}, handlers::qualify_path::Qu // ``` // struct Foo; // impl Foo { -// fn foo(&self) {} +// fn foo(&self) {} // } // fn main() { // let foo = Foo; @@ -32,7 +38,7 @@ use crate::{assist_context::{AssistContext, Assists}, handlers::qualify_path::Qu pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let fn_name = &call.name_ref()?; - + // let callExpr = path_expr.syntax(); let range = call.syntax().text_range(); let resolved_call = ctx.sema.resolve_method_call(&call)?; @@ -40,10 +46,11 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Opt 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 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 call `{}`", fn_name), @@ -52,17 +59,17 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Opt qualify_candidate.qualify( |replace_with: String| builder.replace(range, replace_with), &receiver_path, - item_in_ns + item_in_ns, ) - } + }, ); Some(()) } #[cfg(test)] mod tests { - use crate::tests::{check_assist}; use super::*; + use crate::tests::check_assist; #[test] fn struct_method() { @@ -478,4 +485,3 @@ fn main() { ); } } - diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index ecfced83b1..a0dbf9f7b3 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs @@ -1,7 +1,10 @@ use std::iter; use hir::AsAssocItem; -use ide_db::helpers::{import_assets::{ImportCandidate, LocatedImport}, mod_path_to_ast}; +use ide_db::helpers::{ + import_assets::{ImportCandidate, LocatedImport}, + mod_path_to_ast, +}; use ide_db::RootDatabase; use syntax::{ ast, @@ -136,7 +139,7 @@ impl QualifyCandidate<'_> { ) -> Option<()> { let receiver = mcall_expr.receiver()?; let method_name = mcall_expr.name_ref()?; - let generics = + let generics = mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string); let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); From 9f31f59fdfb11805c1986c1f0b8123b7e907089b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Palenica?= Date: Wed, 20 Oct 2021 23:37:31 -0700 Subject: [PATCH 3/7] Cleanup - remove unnecessary pub --- crates/ide_db/src/helpers/import_assets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 7170e14d84..f1e8ee4935 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -619,6 +619,6 @@ fn path_import_candidate( }) } -pub fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option { +fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option { item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)) } From b3d92052ceba67cefd4e9136320f020f58ae11e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Palenica?= Date: Wed, 20 Oct 2021 23:38:28 -0700 Subject: [PATCH 4/7] Remove comment --- crates/ide_assists/src/handlers/qualify_method_call.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ide_assists/src/handlers/qualify_method_call.rs b/crates/ide_assists/src/handlers/qualify_method_call.rs index 773e912e25..132996234f 100644 --- a/crates/ide_assists/src/handlers/qualify_method_call.rs +++ b/crates/ide_assists/src/handlers/qualify_method_call.rs @@ -39,7 +39,6 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Opt let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let fn_name = &call.name_ref()?; - // let callExpr = path_expr.syntax(); let range = call.syntax().text_range(); let resolved_call = ctx.sema.resolve_method_call(&call)?; From c2fd0c48a6c0446e326985994d25bf9efe991744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Palenica?= Date: Wed, 20 Oct 2021 23:39:25 -0700 Subject: [PATCH 5/7] cleanup qualify_path --- crates/ide_assists/src/handlers/qualify_path.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index a0dbf9f7b3..1f2a115b29 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs @@ -37,7 +37,6 @@ use crate::{ // ``` pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; - let mut proposed_imports = import_assets.search_for_relative_paths(&ctx.sema); if proposed_imports.is_empty() { return None; @@ -92,7 +91,6 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> } Some(()) } -#[derive(Debug)] pub(crate) enum QualifyCandidate<'db> { QualifierStart(ast::PathSegment, Option), UnqualifiedName(Option), From 91988f46b7618c89652632f7052265e5d2b5cae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Palenica?= Date: Wed, 20 Oct 2021 23:54:22 -0700 Subject: [PATCH 6/7] Add generated docs --- crates/ide_assists/src/tests/generated.rs | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 6c5eaf310c..bb058bb8f2 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -1504,6 +1504,33 @@ fn main() { ) } +#[test] +fn doctest_qualify_method_call() { + check_doc_test( + "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 doctest_qualify_path() { check_doc_test( From bfc86f64c3f1498ce0939ab75e301a20cb03b36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Palenica?= Date: Thu, 21 Oct 2021 23:41:43 -0700 Subject: [PATCH 7/7] apply code review suggestions --- .../src/handlers/qualify_method_call.rs | 55 +++++++++++++++++-- .../ide_assists/src/handlers/qualify_path.rs | 3 +- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/crates/ide_assists/src/handlers/qualify_method_call.rs b/crates/ide_assists/src/handlers/qualify_method_call.rs index 132996234f..43878879ed 100644 --- a/crates/ide_assists/src/handlers/qualify_method_call.rs +++ b/crates/ide_assists/src/handlers/qualify_method_call.rs @@ -12,7 +12,7 @@ use crate::{ // Assist: qualify_method_call // -// If the name is resolvable, provides fully qualified path for it. +// Replaces the method call with a qualified function call. // // ``` // struct Foo; @@ -36,8 +36,10 @@ use crate::{ // } // ``` pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; - let fn_name = &call.name_ref()?; + 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)?; @@ -52,7 +54,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Opt acc.add( AssistId("qualify_method_call", AssistKind::RefactorInline), - format!("Qualify call `{}`", fn_name), + format!("Qualify `{}` method call", ident.text()), range, |builder| { qualify_candidate.qualify( @@ -68,7 +70,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Opt #[cfg(test)] mod tests { use super::*; - use crate::tests::check_assist; + use crate::tests::{check_assist, check_assist_not_applicable}; #[test] fn struct_method() { @@ -480,6 +482,49 @@ 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() +} "#, ); } diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index 1f2a115b29..29f2c785bc 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs @@ -171,8 +171,7 @@ impl QualifyCandidate<'_> { let trait_method_name = mcall_expr.name_ref()?; let trait_ = item_as_trait(db, item)?; let method = find_trait_method(db, trait_, &trait_method_name)?; - Self::qualify_fn_call(db, mcall_expr, replacer, import, &method); - Some(()) + Self::qualify_fn_call(db, mcall_expr, replacer, import, &method) } }