--- /dev/null
+use ide_db::{
+ helpers::{mod_path_to_ast, FamousDefs},
+ traits::resolve_target_trait,
+};
+use syntax::ast::{self, AstNode, NameOwner};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: convert_into_to_from
+//
+// Converts an Into impl to an equivalent From impl.
+//
+// ```
+// # //- /lib.rs crate:core
+// # pub mod convert { pub trait Into<T> { pub fn into(self) -> T; } }
+// # //- /lib.rs crate:main deps:core
+// # use core::convert::Into;
+// impl $0Into<Thing> for usize {
+// fn into(self) -> Thing {
+// Thing {
+// b: self.to_string(),
+// a: self
+// }
+// }
+// }
+// ```
+// ->
+// ```
+// # use core::convert::Into;
+// impl From<usize> for Thing {
+// fn from(val: usize) -> Self {
+// Thing {
+// b: val.to_string(),
+// a: val
+// }
+// }
+// }
+// ```
+pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+ let impl_ = ctx.find_node_at_offset::<ast::Impl>()?;
+ let src_type = impl_.self_ty()?;
+ let ast_trait = impl_.trait_()?;
+
+ let module = ctx.sema.scope(impl_.syntax()).module()?;
+
+ let trait_ = resolve_target_trait(&ctx.sema, &impl_)?;
+ if trait_ != FamousDefs(&ctx.sema, Some(module.krate())).core_convert_Into()? {
+ return None;
+ }
+
+ let src_type_path = {
+ let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?;
+ let src_type_def = match ctx.sema.resolve_path(&src_type_path) {
+ Some(hir::PathResolution::Def(module_def)) => module_def,
+ _ => return None,
+ };
+
+ mod_path_to_ast(&module.find_use_path(ctx.db(), src_type_def)?)
+ };
+
+ let dest_type = match &ast_trait {
+ ast::Type::PathType(path) => {
+ path.path()?.segment()?.generic_arg_list()?.generic_args().next()?
+ }
+ _ => return None,
+ };
+
+ let into_fn = impl_.assoc_item_list()?.assoc_items().find_map(|item| {
+ if let ast::AssocItem::Fn(f) = item {
+ if f.name()?.text() == "into" {
+ return Some(f);
+ }
+ };
+ None
+ })?;
+
+ let into_fn_name = into_fn.name()?;
+ let into_fn_params = into_fn.param_list()?;
+ let into_fn_return = into_fn.ret_type()?;
+
+ let selfs = into_fn
+ .body()?
+ .syntax()
+ .descendants()
+ .filter_map(ast::NameRef::cast)
+ .filter(|name| name.text() == "self" || name.text() == "Self");
+
+ acc.add(
+ AssistId("convert_into_to_from", AssistKind::RefactorRewrite),
+ "Convert Into to From",
+ impl_.syntax().text_range(),
+ |builder| {
+ builder.replace(src_type.syntax().text_range(), dest_type.to_string());
+ builder.replace(ast_trait.syntax().text_range(), format!("From<{}>", src_type));
+ builder.replace(into_fn_return.syntax().text_range(), "-> Self");
+ builder.replace(
+ into_fn_params.syntax().text_range(),
+ format!("(val: {})", src_type.to_string()),
+ );
+ builder.replace(into_fn_name.syntax().text_range(), "from");
+
+ for s in selfs {
+ match s.text().as_ref() {
+ "self" => builder.replace(s.syntax().text_range(), "val"),
+ "Self" => builder.replace(s.syntax().text_range(), src_type_path.to_string()),
+ _ => {}
+ }
+ }
+ },
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::tests::check_assist;
+
+ #[test]
+ fn convert_into_to_from_converts_a_struct() {
+ check_convert_into_to_from(
+ r#"
+struct Thing {
+ a: String,
+ b: usize
+}
+
+impl $0core::convert::Into<Thing> for usize {
+ fn into(self) -> Thing {
+ Thing {
+ b: self.to_string(),
+ a: self
+ }
+ }
+}
+"#,
+ r#"
+struct Thing {
+ a: String,
+ b: usize
+}
+
+impl From<usize> for Thing {
+ fn from(val: usize) -> Self {
+ Thing {
+ b: val.to_string(),
+ a: val
+ }
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn convert_into_to_from_converts_enums() {
+ check_convert_into_to_from(
+ r#"
+enum Thing {
+ Foo(String),
+ Bar(String)
+}
+
+impl $0core::convert::Into<String> for Thing {
+ fn into(self) -> String {
+ match self {
+ Self::Foo(s) => s,
+ Self::Bar(s) => s
+ }
+ }
+}
+"#,
+ r#"
+enum Thing {
+ Foo(String),
+ Bar(String)
+}
+
+impl From<Thing> for String {
+ fn from(val: Thing) -> Self {
+ match val {
+ Thing::Foo(s) => s,
+ Thing::Bar(s) => s
+ }
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn convert_into_to_from_on_enum_with_lifetimes() {
+ check_convert_into_to_from(
+ r#"
+enum Thing<'a> {
+ Foo(&'a str),
+ Bar(&'a str)
+}
+
+impl<'a> $0core::convert::Into<&'a str> for Thing<'a> {
+ fn into(self) -> &'a str {
+ match self {
+ Self::Foo(s) => s,
+ Self::Bar(s) => s
+ }
+ }
+}
+"#,
+ r#"
+enum Thing<'a> {
+ Foo(&'a str),
+ Bar(&'a str)
+}
+
+impl<'a> From<Thing<'a>> for &'a str {
+ fn from(val: Thing<'a>) -> Self {
+ match val {
+ Thing::Foo(s) => s,
+ Thing::Bar(s) => s
+ }
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn convert_into_to_from_works_on_references() {
+ check_convert_into_to_from(
+ r#"
+struct Thing(String);
+
+impl $0core::convert::Into<String> for &Thing {
+ fn into(self) -> Thing {
+ self.0.clone()
+ }
+}
+"#,
+ r#"
+struct Thing(String);
+
+impl From<&Thing> for String {
+ fn from(val: &Thing) -> Self {
+ val.0.clone()
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn convert_into_to_from_works_on_qualified_structs() {
+ check_convert_into_to_from(
+ r#"
+mod things {
+ pub struct Thing(String);
+ pub struct BetterThing(String);
+}
+
+impl $0core::convert::Into<things::BetterThing> for &things::Thing {
+ fn into(self) -> Thing {
+ things::BetterThing(self.0.clone())
+ }
+}
+"#,
+ r#"
+mod things {
+ pub struct Thing(String);
+ pub struct BetterThing(String);
+}
+
+impl From<&things::Thing> for things::BetterThing {
+ fn from(val: &things::Thing) -> Self {
+ things::BetterThing(val.0.clone())
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn convert_into_to_from_works_on_qualified_enums() {
+ check_convert_into_to_from(
+ r#"
+mod things {
+ pub enum Thing {
+ A(String)
+ }
+ pub struct BetterThing {
+ B(String)
+ }
+}
+
+impl $0core::convert::Into<things::BetterThing> for &things::Thing {
+ fn into(self) -> Thing {
+ match self {
+ Self::A(s) => things::BetterThing::B(s)
+ }
+ }
+}
+"#,
+ r#"
+mod things {
+ pub enum Thing {
+ A(String)
+ }
+ pub struct BetterThing {
+ B(String)
+ }
+}
+
+impl From<&things::Thing> for things::BetterThing {
+ fn from(val: &things::Thing) -> Self {
+ match val {
+ things::Thing::A(s) => things::BetterThing::B(s)
+ }
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn convert_into_to_from_not_applicable_on_any_trait_named_into() {
+ check_assist_not_applicable(
+ r#"
+pub trait Into<T> {{
+ pub fn into(self) -> T;
+}}
+
+struct Thing {
+ a: String,
+}
+
+impl $0Into<Thing> for String {
+ fn into(self) -> Thing {
+ Thing {
+ a: self
+ }
+ }
+}
+"#,
+ );
+ }
+
+ fn check_convert_into_to_from(before: &str, after: &str) {
+ let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
+ check_assist(convert_into_to_from, before, after);
+ }
+
+ fn check_assist_not_applicable(before: &str) {
+ let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
+ crate::tests::check_assist_not_applicable(convert_into_to_from, before);
+ }
+}