ide_assists/handlers/
into_to_qualified_from.rs

1use hir::{AsAssocItem, HirDisplay};
2use ide_db::{assists::AssistId, famous_defs::FamousDefs};
3use syntax::{AstNode, ast};
4
5use crate::assist_context::{AssistContext, Assists};
6
7// Assist: into_to_qualified_from
8//
9// Convert an `into` method call to a fully qualified `from` call.
10//
11// ```
12// //- minicore: from
13// struct B;
14// impl From<i32> for B {
15//     fn from(a: i32) -> Self {
16//        B
17//     }
18// }
19//
20// fn main() -> () {
21//     let a = 3;
22//     let b: B = a.in$0to();
23// }
24// ```
25// ->
26// ```
27// struct B;
28// impl From<i32> for B {
29//     fn from(a: i32) -> Self {
30//        B
31//     }
32// }
33//
34// fn main() -> () {
35//     let a = 3;
36//     let b: B = B::from(a);
37// }
38// ```
39pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
40    let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
41    let nameref = method_call.name_ref()?;
42    let receiver = method_call.receiver()?;
43    let db = ctx.db();
44    let sema = &ctx.sema;
45    let fnc = sema.resolve_method_call(&method_call)?;
46    let scope = sema.scope(method_call.syntax())?;
47    // Check if the method call refers to Into trait.
48    if fnc.as_assoc_item(db)?.implemented_trait(db)?
49        == FamousDefs(sema, scope.krate()).core_convert_Into()?
50    {
51        let type_call = sema.type_of_expr(&method_call.clone().into())?;
52        let adjusted_tc = type_call.adjusted();
53
54        if adjusted_tc.contains_unknown() {
55            return None;
56        }
57
58        let sc = adjusted_tc.display_source_code(db, scope.module().into(), true).ok()?;
59        acc.add(
60            AssistId::generate("into_to_qualified_from"),
61            "Convert `into` to fully qualified `from`",
62            nameref.syntax().text_range(),
63            |edit| {
64                edit.replace(
65                    method_call.syntax().text_range(),
66                    if sc.chars().all(|c| c.is_alphanumeric() || c == ':') {
67                        format!("{sc}::from({receiver})")
68                    } else {
69                        format!("<{sc}>::from({receiver})")
70                    },
71                );
72            },
73        );
74    }
75
76    Some(())
77}
78
79#[cfg(test)]
80mod tests {
81    use crate::tests::check_assist;
82
83    use super::into_to_qualified_from;
84
85    #[test]
86    fn two_types_in_same_mod() {
87        check_assist(
88            into_to_qualified_from,
89            r#"
90//- minicore: from
91struct A;
92struct B;
93impl From<A> for B {
94    fn from(a: A) -> Self {
95        B
96    }
97}
98
99fn main() -> () {
100    let a: A = A;
101    let b: B = a.in$0to();
102}"#,
103            r#"
104struct A;
105struct B;
106impl From<A> for B {
107    fn from(a: A) -> Self {
108        B
109    }
110}
111
112fn main() -> () {
113    let a: A = A;
114    let b: B = B::from(a);
115}"#,
116        )
117    }
118
119    #[test]
120    fn from_in_child_mod_imported() {
121        check_assist(
122            into_to_qualified_from,
123            r#"
124//- minicore: from
125use C::B;
126
127struct A;
128
129mod C {
130    use crate::A;
131
132    pub(super) struct B;
133    impl From<A> for B {
134        fn from(a: A) -> Self {
135            B
136        }
137    }
138}
139
140fn main() -> () {
141    let a: A = A;
142    let b: B = a.in$0to();
143}"#,
144            r#"
145use C::B;
146
147struct A;
148
149mod C {
150    use crate::A;
151
152    pub(super) struct B;
153    impl From<A> for B {
154        fn from(a: A) -> Self {
155            B
156        }
157    }
158}
159
160fn main() -> () {
161    let a: A = A;
162    let b: B = B::from(a);
163}"#,
164        )
165    }
166
167    #[test]
168    fn from_in_child_mod_not_imported() {
169        check_assist(
170            into_to_qualified_from,
171            r#"
172//- minicore: from
173struct A;
174
175mod C {
176    use crate::A;
177
178    pub(super) struct B;
179    impl From<A> for B {
180        fn from(a: A) -> Self {
181            B
182        }
183    }
184}
185
186fn main() -> () {
187    let a: A = A;
188    let b: C::B = a.in$0to();
189}"#,
190            r#"
191struct A;
192
193mod C {
194    use crate::A;
195
196    pub(super) struct B;
197    impl From<A> for B {
198        fn from(a: A) -> Self {
199            B
200        }
201    }
202}
203
204fn main() -> () {
205    let a: A = A;
206    let b: C::B = C::B::from(a);
207}"#,
208        )
209    }
210
211    #[test]
212    fn preceding_type_qualifier() {
213        check_assist(
214            into_to_qualified_from,
215            r#"
216//- minicore: from
217impl From<(i32,i32)> for [i32;2] {
218    fn from(value: (i32,i32)) -> Self {
219        [value.0, value.1]
220    }
221}
222
223fn tuple_to_array() -> [i32; 2] {
224    (0,1).in$0to()
225}"#,
226            r#"
227impl From<(i32,i32)> for [i32;2] {
228    fn from(value: (i32,i32)) -> Self {
229        [value.0, value.1]
230    }
231}
232
233fn tuple_to_array() -> [i32; 2] {
234    <[i32; 2]>::from((0,1))
235}"#,
236        )
237    }
238
239    #[test]
240    fn type_with_gens() {
241        check_assist(
242            into_to_qualified_from,
243            r#"
244//- minicore: from
245struct StructA<Gen>(Gen);
246
247impl From<i32> for StructA<i32> {
248    fn from(value: i32) -> Self {
249        StructA(value + 1)
250    }
251}
252
253fn main() -> () {
254    let a: StructA<i32> = 3.in$0to();
255}"#,
256            r#"
257struct StructA<Gen>(Gen);
258
259impl From<i32> for StructA<i32> {
260    fn from(value: i32) -> Self {
261        StructA(value + 1)
262    }
263}
264
265fn main() -> () {
266    let a: StructA<i32> = <StructA<i32>>::from(3);
267}"#,
268        )
269    }
270}