274 lines
5.0 KiB
Rust
274 lines
5.0 KiB
Rust
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 adjusted_tc = type_call.adjusted();
|
|
|
|
if adjusted_tc.contains_unknown() {
|
|
return None;
|
|
}
|
|
|
|
let sc = adjusted_tc.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(),
|
|
if sc.chars().all(|c| c.is_alphanumeric() || c == ':') {
|
|
format!("{}::from({})", sc, receiver)
|
|
} else {
|
|
format!("<{}>::from({})", sc, 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);
|
|
}"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn preceding_type_qualifier() {
|
|
check_assist(
|
|
into_to_qualified_from,
|
|
r#"
|
|
//- minicore: from
|
|
impl From<(i32,i32)> for [i32;2] {
|
|
fn from(value: (i32,i32)) -> Self {
|
|
[value.0, value.1]
|
|
}
|
|
}
|
|
|
|
fn tuple_to_array() -> [i32; 2] {
|
|
(0,1).in$0to()
|
|
}"#,
|
|
r#"
|
|
impl From<(i32,i32)> for [i32;2] {
|
|
fn from(value: (i32,i32)) -> Self {
|
|
[value.0, value.1]
|
|
}
|
|
}
|
|
|
|
fn tuple_to_array() -> [i32; 2] {
|
|
<[i32; 2]>::from((0,1))
|
|
}"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn type_with_gens() {
|
|
check_assist(
|
|
into_to_qualified_from,
|
|
r#"
|
|
//- minicore: from
|
|
struct StructA<Gen>(Gen);
|
|
|
|
impl From<i32> for StructA<i32> {
|
|
fn from(value: i32) -> Self {
|
|
StructA(value + 1)
|
|
}
|
|
}
|
|
|
|
fn main() -> () {
|
|
let a: StructA<i32> = 3.in$0to();
|
|
}"#,
|
|
r#"
|
|
struct StructA<Gen>(Gen);
|
|
|
|
impl From<i32> for StructA<i32> {
|
|
fn from(value: i32) -> Self {
|
|
StructA(value + 1)
|
|
}
|
|
}
|
|
|
|
fn main() -> () {
|
|
let a: StructA<i32> = <StructA<i32>>::from(3);
|
|
}"#,
|
|
)
|
|
}
|
|
}
|