2 helpers::{mod_path_to_ast, FamousDefs},
3 traits::resolve_target_trait,
5 use syntax::ast::{self, AstNode, NameOwner};
7 use crate::{AssistContext, AssistId, AssistKind, Assists};
9 // Assist: convert_into_to_from
11 // Converts an Into impl to an equivalent From impl.
14 // # //- /lib.rs crate:core
15 // # pub mod convert { pub trait Into<T> { pub fn into(self) -> T; } }
16 // # //- /lib.rs crate:main deps:core
17 // # use core::convert::Into;
18 // impl $0Into<Thing> for usize {
19 // fn into(self) -> Thing {
21 // b: self.to_string(),
29 // # use core::convert::Into;
30 // impl From<usize> for Thing {
31 // fn from(val: usize) -> Self {
33 // b: val.to_string(),
39 pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let impl_ = ctx.find_node_at_offset::<ast::Impl>()?;
41 let src_type = impl_.self_ty()?;
42 let ast_trait = impl_.trait_()?;
44 let module = ctx.sema.scope(impl_.syntax()).module()?;
46 let trait_ = resolve_target_trait(&ctx.sema, &impl_)?;
47 if trait_ != FamousDefs(&ctx.sema, Some(module.krate())).core_convert_Into()? {
52 let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?;
53 let src_type_def = match ctx.sema.resolve_path(&src_type_path) {
54 Some(hir::PathResolution::Def(module_def)) => module_def,
58 mod_path_to_ast(&module.find_use_path(ctx.db(), src_type_def)?)
61 let dest_type = match &ast_trait {
62 ast::Type::PathType(path) => {
63 path.path()?.segment()?.generic_arg_list()?.generic_args().next()?
68 let into_fn = impl_.assoc_item_list()?.assoc_items().find_map(|item| {
69 if let ast::AssocItem::Fn(f) = item {
70 if f.name()?.text() == "into" {
77 let into_fn_name = into_fn.name()?;
78 let into_fn_params = into_fn.param_list()?;
79 let into_fn_return = into_fn.ret_type()?;
85 .filter_map(ast::NameRef::cast)
86 .filter(|name| name.text() == "self" || name.text() == "Self");
89 AssistId("convert_into_to_from", AssistKind::RefactorRewrite),
90 "Convert Into to From",
91 impl_.syntax().text_range(),
93 builder.replace(src_type.syntax().text_range(), dest_type.to_string());
94 builder.replace(ast_trait.syntax().text_range(), format!("From<{}>", src_type));
95 builder.replace(into_fn_return.syntax().text_range(), "-> Self");
97 into_fn_params.syntax().text_range(),
98 format!("(val: {})", src_type.to_string()),
100 builder.replace(into_fn_name.syntax().text_range(), "from");
103 match s.text().as_ref() {
104 "self" => builder.replace(s.syntax().text_range(), "val"),
105 "Self" => builder.replace(s.syntax().text_range(), src_type_path.to_string()),
117 use crate::tests::check_assist;
120 fn convert_into_to_from_converts_a_struct() {
121 check_convert_into_to_from(
128 impl $0core::convert::Into<Thing> for usize {
129 fn into(self) -> Thing {
143 impl From<usize> for Thing {
144 fn from(val: usize) -> Self {
156 fn convert_into_to_from_converts_enums() {
157 check_convert_into_to_from(
164 impl $0core::convert::Into<String> for Thing {
165 fn into(self) -> String {
179 impl From<Thing> for String {
180 fn from(val: Thing) -> Self {
192 fn convert_into_to_from_on_enum_with_lifetimes() {
193 check_convert_into_to_from(
200 impl<'a> $0core::convert::Into<&'a str> for Thing<'a> {
201 fn into(self) -> &'a str {
215 impl<'a> From<Thing<'a>> for &'a str {
216 fn from(val: Thing<'a>) -> Self {
228 fn convert_into_to_from_works_on_references() {
229 check_convert_into_to_from(
231 struct Thing(String);
233 impl $0core::convert::Into<String> for &Thing {
234 fn into(self) -> Thing {
240 struct Thing(String);
242 impl From<&Thing> for String {
243 fn from(val: &Thing) -> Self {
252 fn convert_into_to_from_works_on_qualified_structs() {
253 check_convert_into_to_from(
256 pub struct Thing(String);
257 pub struct BetterThing(String);
260 impl $0core::convert::Into<things::BetterThing> for &things::Thing {
261 fn into(self) -> Thing {
262 things::BetterThing(self.0.clone())
268 pub struct Thing(String);
269 pub struct BetterThing(String);
272 impl From<&things::Thing> for things::BetterThing {
273 fn from(val: &things::Thing) -> Self {
274 things::BetterThing(val.0.clone())
282 fn convert_into_to_from_works_on_qualified_enums() {
283 check_convert_into_to_from(
289 pub struct BetterThing {
294 impl $0core::convert::Into<things::BetterThing> for &things::Thing {
295 fn into(self) -> Thing {
297 Self::A(s) => things::BetterThing::B(s)
307 pub struct BetterThing {
312 impl From<&things::Thing> for things::BetterThing {
313 fn from(val: &things::Thing) -> Self {
315 things::Thing::A(s) => things::BetterThing::B(s)
324 fn convert_into_to_from_not_applicable_on_any_trait_named_into() {
325 check_assist_not_applicable(
328 pub fn into(self) -> T;
335 impl $0Into<Thing> for String {
336 fn into(self) -> Thing {
346 fn check_convert_into_to_from(before: &str, after: &str) {
347 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
348 check_assist(convert_into_to_from, before, after);
351 fn check_assist_not_applicable(before: &str) {
352 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
353 crate::tests::check_assist_not_applicable(convert_into_to_from, before);