]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
Rollup merge of #99460 - JanBeh:PR_asref_asmut_docs, r=joshtriplett
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / introduce_named_generic.rs
1 use syntax::{
2     ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode},
3     ted,
4 };
5
6 use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
7
8 // Assist: introduce_named_generic
9 //
10 // Replaces `impl Trait` function argument with the named generic.
11 //
12 // ```
13 // fn foo(bar: $0impl Bar) {}
14 // ```
15 // ->
16 // ```
17 // fn foo<B: Bar>(bar: B) {}
18 // ```
19 pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
20     let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
21     let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?;
22     let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?;
23
24     let type_bound_list = impl_trait_type.type_bound_list()?;
25
26     let target = fn_.syntax().text_range();
27     acc.add(
28         AssistId("introduce_named_generic", AssistKind::RefactorRewrite),
29         "Replace impl trait with generic",
30         target,
31         |edit| {
32             let impl_trait_type = edit.make_mut(impl_trait_type);
33             let fn_ = edit.make_mut(fn_);
34
35             let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type);
36
37             let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
38                 .clone_for_update();
39             let new_ty = make::ty(&type_param_name).clone_for_update();
40
41             ted::replace(impl_trait_type.syntax(), new_ty.syntax());
42             fn_.get_or_create_generic_param_list().add_generic_param(type_param.into())
43         },
44     )
45 }
46
47 #[cfg(test)]
48 mod tests {
49     use super::*;
50
51     use crate::tests::check_assist;
52
53     #[test]
54     fn introduce_named_generic_params() {
55         check_assist(
56             introduce_named_generic,
57             r#"fn foo<G>(bar: $0impl Bar) {}"#,
58             r#"fn foo<G, B: Bar>(bar: B) {}"#,
59         );
60     }
61
62     #[test]
63     fn replace_impl_trait_without_generic_params() {
64         check_assist(
65             introduce_named_generic,
66             r#"fn foo(bar: $0impl Bar) {}"#,
67             r#"fn foo<B: Bar>(bar: B) {}"#,
68         );
69     }
70
71     #[test]
72     fn replace_two_impl_trait_with_generic_params() {
73         check_assist(
74             introduce_named_generic,
75             r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
76             r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#,
77         );
78     }
79
80     #[test]
81     fn replace_impl_trait_with_empty_generic_params() {
82         check_assist(
83             introduce_named_generic,
84             r#"fn foo<>(bar: $0impl Bar) {}"#,
85             r#"fn foo<B: Bar>(bar: B) {}"#,
86         );
87     }
88
89     #[test]
90     fn replace_impl_trait_with_empty_multiline_generic_params() {
91         check_assist(
92             introduce_named_generic,
93             r#"
94 fn foo<
95 >(bar: $0impl Bar) {}
96 "#,
97             r#"
98 fn foo<B: Bar
99 >(bar: B) {}
100 "#,
101         );
102     }
103
104     #[test]
105     fn replace_impl_trait_with_exist_generic_letter() {
106         // FIXME: This is wrong, we should pick a different name if the one we
107         // want is already bound.
108         check_assist(
109             introduce_named_generic,
110             r#"fn foo<B>(bar: $0impl Bar) {}"#,
111             r#"fn foo<B, B: Bar>(bar: B) {}"#,
112         );
113     }
114
115     #[test]
116     fn replace_impl_trait_with_multiline_generic_params() {
117         check_assist(
118             introduce_named_generic,
119             r#"
120 fn foo<
121     G: Foo,
122     F,
123     H,
124 >(bar: $0impl Bar) {}
125 "#,
126             r#"
127 fn foo<
128     G: Foo,
129     F,
130     H, B: Bar,
131 >(bar: B) {}
132 "#,
133         );
134     }
135
136     #[test]
137     fn replace_impl_trait_multiple() {
138         check_assist(
139             introduce_named_generic,
140             r#"fn foo(bar: $0impl Foo + Bar) {}"#,
141             r#"fn foo<F: Foo + Bar>(bar: F) {}"#,
142         );
143     }
144 }