aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers/replace_impl_trait_with_generic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/handlers/replace_impl_trait_with_generic.rs')
-rw-r--r--crates/assists/src/handlers/replace_impl_trait_with_generic.rs168
1 files changed, 168 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs
new file mode 100644
index 000000000..6738bc134
--- /dev/null
+++ b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs
@@ -0,0 +1,168 @@
1use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner};
2
3use crate::{AssistContext, AssistId, AssistKind, Assists};
4
5// Assist: replace_impl_trait_with_generic
6//
7// Replaces `impl Trait` function argument with the named generic.
8//
9// ```
10// fn foo(bar: <|>impl Bar) {}
11// ```
12// ->
13// ```
14// fn foo<B: Bar>(bar: B) {}
15// ```
16pub(crate) fn replace_impl_trait_with_generic(
17 acc: &mut Assists,
18 ctx: &AssistContext,
19) -> Option<()> {
20 let type_impl_trait = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
21 let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?;
22 let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?;
23
24 let impl_trait_ty = type_impl_trait.type_bound_list()?;
25
26 let target = type_fn.syntax().text_range();
27 acc.add(
28 AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite),
29 "Replace impl trait with generic",
30 target,
31 |edit| {
32 let generic_letter = impl_trait_ty.to_string().chars().next().unwrap().to_string();
33
34 let generic_param_list = type_fn
35 .generic_param_list()
36 .unwrap_or_else(|| make::generic_param_list(None))
37 .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty)));
38
39 let new_type_fn = type_fn
40 .replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter))
41 .with_generic_param_list(generic_param_list);
42
43 edit.replace_ast(type_fn.clone(), new_type_fn);
44 },
45 )
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 use crate::tests::check_assist;
53
54 #[test]
55 fn replace_impl_trait_with_generic_params() {
56 check_assist(
57 replace_impl_trait_with_generic,
58 r#"
59 fn foo<G>(bar: <|>impl Bar) {}
60 "#,
61 r#"
62 fn foo<G, B: Bar>(bar: B) {}
63 "#,
64 );
65 }
66
67 #[test]
68 fn replace_impl_trait_without_generic_params() {
69 check_assist(
70 replace_impl_trait_with_generic,
71 r#"
72 fn foo(bar: <|>impl Bar) {}
73 "#,
74 r#"
75 fn foo<B: Bar>(bar: B) {}
76 "#,
77 );
78 }
79
80 #[test]
81 fn replace_two_impl_trait_with_generic_params() {
82 check_assist(
83 replace_impl_trait_with_generic,
84 r#"
85 fn foo<G>(foo: impl Foo, bar: <|>impl Bar) {}
86 "#,
87 r#"
88 fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}
89 "#,
90 );
91 }
92
93 #[test]
94 fn replace_impl_trait_with_empty_generic_params() {
95 check_assist(
96 replace_impl_trait_with_generic,
97 r#"
98 fn foo<>(bar: <|>impl Bar) {}
99 "#,
100 r#"
101 fn foo<B: Bar>(bar: B) {}
102 "#,
103 );
104 }
105
106 #[test]
107 fn replace_impl_trait_with_empty_multiline_generic_params() {
108 check_assist(
109 replace_impl_trait_with_generic,
110 r#"
111 fn foo<
112 >(bar: <|>impl Bar) {}
113 "#,
114 r#"
115 fn foo<B: Bar
116 >(bar: B) {}
117 "#,
118 );
119 }
120
121 #[test]
122 #[ignore = "This case is very rare but there is no simple solutions to fix it."]
123 fn replace_impl_trait_with_exist_generic_letter() {
124 check_assist(
125 replace_impl_trait_with_generic,
126 r#"
127 fn foo<B>(bar: <|>impl Bar) {}
128 "#,
129 r#"
130 fn foo<B, C: Bar>(bar: C) {}
131 "#,
132 );
133 }
134
135 #[test]
136 fn replace_impl_trait_with_multiline_generic_params() {
137 check_assist(
138 replace_impl_trait_with_generic,
139 r#"
140 fn foo<
141 G: Foo,
142 F,
143 H,
144 >(bar: <|>impl Bar) {}
145 "#,
146 r#"
147 fn foo<
148 G: Foo,
149 F,
150 H, B: Bar
151 >(bar: B) {}
152 "#,
153 );
154 }
155
156 #[test]
157 fn replace_impl_trait_multiple() {
158 check_assist(
159 replace_impl_trait_with_generic,
160 r#"
161 fn foo(bar: <|>impl Foo + Bar) {}
162 "#,
163 r#"
164 fn foo<F: Foo + Bar>(bar: F) {}
165 "#,
166 );
167 }
168}