1 use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast, traits::resolve_target_trait};
2 use syntax::ast::{self, AstNode, HasName};
4 use crate::{AssistContext, AssistId, AssistKind, Assists};
6 // FIXME: this should be a diagnostic
8 // Assist: convert_into_to_from
10 // Converts an Into impl to an equivalent From impl.
13 // # //- minicore: from
14 // impl $0Into<Thing> for usize {
15 // fn into(self) -> Thing {
17 // b: self.to_string(),
25 // impl From<usize> for Thing {
26 // fn from(val: usize) -> Self {
28 // b: val.to_string(),
34 pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
35 let impl_ = ctx.find_node_at_offset::<ast::Impl>()?;
36 let src_type = impl_.self_ty()?;
37 let ast_trait = impl_.trait_()?;
39 let module = ctx.sema.scope(impl_.syntax())?.module();
41 let trait_ = resolve_target_trait(&ctx.sema, &impl_)?;
42 if trait_ != FamousDefs(&ctx.sema, module.krate()).core_convert_Into()? {
47 let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?;
48 let src_type_def = match ctx.sema.resolve_path(&src_type_path) {
49 Some(hir::PathResolution::Def(module_def)) => module_def,
53 mod_path_to_ast(&module.find_use_path(ctx.db(), src_type_def, ctx.config.prefer_no_std)?)
56 let dest_type = match &ast_trait {
57 ast::Type::PathType(path) => {
58 path.path()?.segment()?.generic_arg_list()?.generic_args().next()?
63 let into_fn = impl_.assoc_item_list()?.assoc_items().find_map(|item| {
64 if let ast::AssocItem::Fn(f) = item {
65 if f.name()?.text() == "into" {
72 let into_fn_name = into_fn.name()?;
73 let into_fn_params = into_fn.param_list()?;
74 let into_fn_return = into_fn.ret_type()?;
80 .filter_map(ast::NameRef::cast)
81 .filter(|name| name.text() == "self" || name.text() == "Self");
84 AssistId("convert_into_to_from", AssistKind::RefactorRewrite),
85 "Convert Into to From",
86 impl_.syntax().text_range(),
88 builder.replace(src_type.syntax().text_range(), dest_type.to_string());
89 builder.replace(ast_trait.syntax().text_range(), format!("From<{src_type}>"));
90 builder.replace(into_fn_return.syntax().text_range(), "-> Self");
91 builder.replace(into_fn_params.syntax().text_range(), format!("(val: {src_type})"));
92 builder.replace(into_fn_name.syntax().text_range(), "from");
95 match s.text().as_ref() {
96 "self" => builder.replace(s.syntax().text_range(), "val"),
97 "Self" => builder.replace(s.syntax().text_range(), src_type_path.to_string()),
109 use crate::tests::{check_assist, check_assist_not_applicable};
112 fn convert_into_to_from_converts_a_struct() {
114 convert_into_to_from,
122 impl $0core::convert::Into<Thing> for usize {
123 fn into(self) -> Thing {
137 impl From<usize> for Thing {
138 fn from(val: usize) -> Self {
150 fn convert_into_to_from_converts_enums() {
152 convert_into_to_from,
160 impl $0core::convert::Into<String> for Thing {
161 fn into(self) -> String {
175 impl From<Thing> for String {
176 fn from(val: Thing) -> Self {
188 fn convert_into_to_from_on_enum_with_lifetimes() {
190 convert_into_to_from,
198 impl<'a> $0core::convert::Into<&'a str> for Thing<'a> {
199 fn into(self) -> &'a str {
213 impl<'a> From<Thing<'a>> for &'a str {
214 fn from(val: Thing<'a>) -> Self {
226 fn convert_into_to_from_works_on_references() {
228 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() {
254 convert_into_to_from,
258 pub struct Thing(String);
259 pub struct BetterThing(String);
262 impl $0core::convert::Into<things::BetterThing> for &things::Thing {
263 fn into(self) -> Thing {
264 things::BetterThing(self.0.clone())
270 pub struct Thing(String);
271 pub struct BetterThing(String);
274 impl From<&things::Thing> for things::BetterThing {
275 fn from(val: &things::Thing) -> Self {
276 things::BetterThing(val.0.clone())
284 fn convert_into_to_from_works_on_qualified_enums() {
286 convert_into_to_from,
293 pub struct BetterThing {
298 impl $0core::convert::Into<things::BetterThing> for &things::Thing {
299 fn into(self) -> Thing {
301 Self::A(s) => things::BetterThing::B(s)
311 pub struct BetterThing {
316 impl From<&things::Thing> for things::BetterThing {
317 fn from(val: &things::Thing) -> Self {
319 things::Thing::A(s) => things::BetterThing::B(s)
328 fn convert_into_to_from_not_applicable_on_any_trait_named_into() {
329 check_assist_not_applicable(
330 convert_into_to_from,
334 pub fn into(self) -> T;
341 impl $0Into<Thing> for String {
342 fn into(self) -> Thing {