about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-09-08 07:44:24 +0000
committerbors <bors@rust-lang.org>2023-09-08 07:44:24 +0000
commit47e0d07eb0dbc35c563ec463199ddc6a3138a06b (patch)
treeb7070b24b1bc629c7adb15a420379e45bfd70ce8
parent5ddad87b1a5ea2c71e3d28bb9f14b2382ca206fa (diff)
parent9762f764aeecd532628ecad91c5e3a49c0a01380 (diff)
downloadrust-47e0d07eb0dbc35c563ec463199ddc6a3138a06b.tar.gz
rust-47e0d07eb0dbc35c563ec463199ddc6a3138a06b.zip
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.
-rw-r--r--crates/ide-assists/src/handlers/into_to_qualified_from.rs205
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs34
3 files changed, 241 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/crates/ide-assists/src/handlers/into_to_qualified_from.rs
new file mode 100644
index 00000000000..663df266b6f
--- /dev/null
+++ b/crates/ide-assists/src/handlers/into_to_qualified_from.rs
@@ -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);
+}"#,
+        )
+    }
+}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 2ebb5ef9b19..7136bdab210 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -211,6 +211,7 @@ mod handlers {
     mod unwrap_result_return_type;
     mod unqualify_method_call;
     mod wrap_return_type_in_result;
+    mod into_to_qualified_from;
 
     pub(crate) fn all() -> &'static [Handler] {
         &[
@@ -274,6 +275,7 @@ mod handlers {
             inline_local_variable::inline_local_variable,
             inline_type_alias::inline_type_alias,
             inline_type_alias::inline_type_alias_uses,
+            into_to_qualified_from::into_to_qualified_from,
             introduce_named_generic::introduce_named_generic,
             introduce_named_lifetime::introduce_named_lifetime,
             invert_if::invert_if,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 8bd87e5f5db..cc132f9a98d 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -1742,6 +1742,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]
 fn doctest_introduce_named_generic() {
     check_doc_test(
         "introduce_named_generic",