rust/crates/ide_assists/src/handlers/qualify_method_call.rs
2022-03-12 16:50:49 +01:00

532 lines
9.5 KiB
Rust

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()
}
"#,
);
}
}