Auto merge of #15573 - alibektas:15539/into_to_from, r=Veykril

assist : `into_to_qualified_from`

fixes #15539. This assist converts an `.into()` call into an explicit fully qualified `from()` call.
This commit is contained in:
bors 2023-09-08 07:44:24 +00:00
commit 47e0d07eb0
3 changed files with 241 additions and 0 deletions

View File

@ -0,0 +1,205 @@
use hir::{AsAssocItem, HirDisplay};
use ide_db::{
assists::{AssistId, AssistKind},
famous_defs::FamousDefs,
};
use syntax::{ast, AstNode};
use crate::assist_context::{AssistContext, Assists};
// Assist: into_to_qualified_from
//
// Convert an `into` method call to a fully qualified `from` call.
//
// ```
// //- minicore: from
// struct B;
// impl From<i32> for B {
// fn from(a: i32) -> Self {
// B
// }
// }
//
// fn main() -> () {
// let a = 3;
// let b: B = a.in$0to();
// }
// ```
// ->
// ```
// struct B;
// impl From<i32> for B {
// fn from(a: i32) -> Self {
// B
// }
// }
//
// fn main() -> () {
// let a = 3;
// let b: B = B::from(a);
// }
// ```
pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
let nameref = method_call.name_ref()?;
let receiver = method_call.receiver()?;
let db = ctx.db();
let sema = &ctx.sema;
let fnc = sema.resolve_method_call(&method_call)?;
let scope = sema.scope(method_call.syntax())?;
// Check if the method call refers to Into trait.
if fnc.as_assoc_item(db)?.containing_trait_impl(db)?
== FamousDefs(sema, scope.krate()).core_convert_Into()?
{
let type_call = sema.type_of_expr(&method_call.clone().into())?;
let type_call_disp =
type_call.adjusted().display_source_code(db, scope.module().into(), true).ok()?;
acc.add(
AssistId("into_to_qualified_from", AssistKind::Generate),
"Convert `into` to fully qualified `from`",
nameref.syntax().text_range(),
|edit| {
edit.replace(
method_call.syntax().text_range(),
format!("{}::from({})", type_call_disp, receiver),
);
},
);
}
Some(())
}
#[cfg(test)]
mod tests {
use crate::tests::check_assist;
use super::into_to_qualified_from;
#[test]
fn two_types_in_same_mod() {
check_assist(
into_to_qualified_from,
r#"
//- minicore: from
struct A;
struct B;
impl From<A> for B {
fn from(a: A) -> Self {
B
}
}
fn main() -> () {
let a: A = A;
let b: B = a.in$0to();
}"#,
r#"
struct A;
struct B;
impl From<A> for B {
fn from(a: A) -> Self {
B
}
}
fn main() -> () {
let a: A = A;
let b: B = B::from(a);
}"#,
)
}
#[test]
fn fromed_in_child_mod_imported() {
check_assist(
into_to_qualified_from,
r#"
//- minicore: from
use C::B;
struct A;
mod C {
use crate::A;
pub(super) struct B;
impl From<A> for B {
fn from(a: A) -> Self {
B
}
}
}
fn main() -> () {
let a: A = A;
let b: B = a.in$0to();
}"#,
r#"
use C::B;
struct A;
mod C {
use crate::A;
pub(super) struct B;
impl From<A> for B {
fn from(a: A) -> Self {
B
}
}
}
fn main() -> () {
let a: A = A;
let b: B = B::from(a);
}"#,
)
}
#[test]
fn fromed_in_child_mod_not_imported() {
check_assist(
into_to_qualified_from,
r#"
//- minicore: from
struct A;
mod C {
use crate::A;
pub(super) struct B;
impl From<A> for B {
fn from(a: A) -> Self {
B
}
}
}
fn main() -> () {
let a: A = A;
let b: C::B = a.in$0to();
}"#,
r#"
struct A;
mod C {
use crate::A;
pub(super) struct B;
impl From<A> for B {
fn from(a: A) -> Self {
B
}
}
}
fn main() -> () {
let a: A = A;
let b: C::B = C::B::from(a);
}"#,
)
}
}

View File

@ -211,6 +211,7 @@ mod handlers {
mod unwrap_result_return_type; mod unwrap_result_return_type;
mod unqualify_method_call; mod unqualify_method_call;
mod wrap_return_type_in_result; mod wrap_return_type_in_result;
mod into_to_qualified_from;
pub(crate) fn all() -> &'static [Handler] { pub(crate) fn all() -> &'static [Handler] {
&[ &[
@ -274,6 +275,7 @@ pub(crate) fn all() -> &'static [Handler] {
inline_local_variable::inline_local_variable, inline_local_variable::inline_local_variable,
inline_type_alias::inline_type_alias, inline_type_alias::inline_type_alias,
inline_type_alias::inline_type_alias_uses, inline_type_alias::inline_type_alias_uses,
into_to_qualified_from::into_to_qualified_from,
introduce_named_generic::introduce_named_generic, introduce_named_generic::introduce_named_generic,
introduce_named_lifetime::introduce_named_lifetime, introduce_named_lifetime::introduce_named_lifetime,
invert_if::invert_if, invert_if::invert_if,

View File

@ -1741,6 +1741,40 @@ fn foo() {
) )
} }
#[test]
fn doctest_into_to_qualified_from() {
check_doc_test(
"into_to_qualified_from",
r#####"
//- minicore: from
struct B;
impl From<i32> for B {
fn from(a: i32) -> Self {
B
}
}
fn main() -> () {
let a = 3;
let b: B = a.in$0to();
}
"#####,
r#####"
struct B;
impl From<i32> for B {
fn from(a: i32) -> Self {
B
}
}
fn main() -> () {
let a = 3;
let b: B = B::from(a);
}
"#####,
)
}
#[test] #[test]
fn doctest_introduce_named_generic() { fn doctest_introduce_named_generic() {
check_doc_test( check_doc_test(