diff options
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r-- | crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | 218 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 |
2 files changed, 220 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs new file mode 100644 index 000000000..cf94a214a --- /dev/null +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | |||
@@ -0,0 +1,218 @@ | |||
1 | use hir::ImplDef; | ||
2 | use ra_syntax::{ | ||
3 | ast::{self, AstNode, NameOwner}, | ||
4 | TextUnit, | ||
5 | }; | ||
6 | use stdx::format_to; | ||
7 | |||
8 | use crate::{Assist, AssistCtx, AssistId}; | ||
9 | use ra_ide_db::RootDatabase; | ||
10 | |||
11 | // Assist add_from_impl_for_enum | ||
12 | // | ||
13 | // Adds a From impl for an enum variant with one tuple field | ||
14 | // | ||
15 | // ``` | ||
16 | // enum A { <|>One(u32) } | ||
17 | // ``` | ||
18 | // -> | ||
19 | // ``` | ||
20 | // enum A { One(u32) } | ||
21 | // | ||
22 | // impl From<u32> for A { | ||
23 | // fn from(v: u32) -> Self { | ||
24 | // A::One(v) | ||
25 | // } | ||
26 | // } | ||
27 | // ``` | ||
28 | pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> { | ||
29 | let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; | ||
30 | let variant_name = variant.name()?; | ||
31 | let enum_name = variant.parent_enum().name()?; | ||
32 | let field_list = match variant.kind() { | ||
33 | ast::StructKind::Tuple(field_list) => field_list, | ||
34 | _ => return None, | ||
35 | }; | ||
36 | if field_list.fields().count() != 1 { | ||
37 | return None; | ||
38 | } | ||
39 | let field_type = field_list.fields().next()?.type_ref()?; | ||
40 | let path = match field_type { | ||
41 | ast::TypeRef::PathType(p) => p, | ||
42 | _ => return None, | ||
43 | }; | ||
44 | |||
45 | if already_has_from_impl(ctx.sema, &variant) { | ||
46 | return None; | ||
47 | } | ||
48 | |||
49 | ctx.add_assist( | ||
50 | AssistId("add_from_impl_for_enum"), | ||
51 | "Add From impl for this enum variant", | ||
52 | |edit| { | ||
53 | let start_offset = variant.parent_enum().syntax().text_range().end(); | ||
54 | let mut buf = String::new(); | ||
55 | format_to!( | ||
56 | buf, | ||
57 | r#" | ||
58 | |||
59 | impl From<{0}> for {1} {{ | ||
60 | fn from(v: {0}) -> Self {{ | ||
61 | {1}::{2}(v) | ||
62 | }} | ||
63 | }}"#, | ||
64 | path.syntax(), | ||
65 | enum_name, | ||
66 | variant_name | ||
67 | ); | ||
68 | edit.insert(start_offset, buf); | ||
69 | edit.set_cursor(start_offset + TextUnit::of_str("\n\n")); | ||
70 | }, | ||
71 | ) | ||
72 | } | ||
73 | |||
74 | fn already_has_from_impl( | ||
75 | sema: &'_ hir::Semantics<'_, RootDatabase>, | ||
76 | variant: &ast::EnumVariant, | ||
77 | ) -> bool { | ||
78 | let scope = sema.scope(&variant.syntax()); | ||
79 | |||
80 | let from_path = ast::make::path_from_text("From"); | ||
81 | let from_hir_path = match hir::Path::from_ast(from_path) { | ||
82 | Some(p) => p, | ||
83 | None => return false, | ||
84 | }; | ||
85 | let from_trait = match scope.resolve_hir_path(&from_hir_path) { | ||
86 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(t))) => t, | ||
87 | _ => return false, | ||
88 | }; | ||
89 | |||
90 | let e: hir::Enum = match sema.to_def(&variant.parent_enum()) { | ||
91 | Some(e) => e, | ||
92 | None => return false, | ||
93 | }; | ||
94 | let e_ty = e.ty(sema.db); | ||
95 | |||
96 | let hir_enum_var: hir::EnumVariant = match sema.to_def(variant) { | ||
97 | Some(ev) => ev, | ||
98 | None => return false, | ||
99 | }; | ||
100 | let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db); | ||
101 | |||
102 | let krate = match scope.module() { | ||
103 | Some(s) => s.krate(), | ||
104 | _ => return false, | ||
105 | }; | ||
106 | let impls = ImplDef::for_trait(sema.db, krate, from_trait); | ||
107 | let imp = impls.iter().find(|imp| { | ||
108 | let targets_enum = imp.target_ty(sema.db) == e_ty; | ||
109 | let param_matches = imp.target_trait_substs_matches(sema.db, &[var_ty.clone()]); | ||
110 | targets_enum && param_matches | ||
111 | }); | ||
112 | |||
113 | imp.is_some() | ||
114 | } | ||
115 | |||
116 | #[cfg(test)] | ||
117 | mod tests { | ||
118 | use super::*; | ||
119 | use crate::helpers::{check_assist, check_assist_not_applicable}; | ||
120 | |||
121 | #[test] | ||
122 | fn test_add_from_impl_for_enum() { | ||
123 | check_assist( | ||
124 | add_from_impl_for_enum, | ||
125 | "enum A { <|>One(u32) }", | ||
126 | r#"enum A { One(u32) } | ||
127 | |||
128 | <|>impl From<u32> for A { | ||
129 | fn from(v: u32) -> Self { | ||
130 | A::One(v) | ||
131 | } | ||
132 | }"#, | ||
133 | ); | ||
134 | } | ||
135 | |||
136 | #[test] | ||
137 | fn test_add_from_impl_for_enum_complicated_path() { | ||
138 | check_assist( | ||
139 | add_from_impl_for_enum, | ||
140 | "enum A { <|>One(foo::bar::baz::Boo) }", | ||
141 | r#"enum A { One(foo::bar::baz::Boo) } | ||
142 | |||
143 | <|>impl From<foo::bar::baz::Boo> for A { | ||
144 | fn from(v: foo::bar::baz::Boo) -> Self { | ||
145 | A::One(v) | ||
146 | } | ||
147 | }"#, | ||
148 | ); | ||
149 | } | ||
150 | |||
151 | #[test] | ||
152 | fn test_add_from_impl_no_element() { | ||
153 | check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One }"); | ||
154 | } | ||
155 | |||
156 | #[test] | ||
157 | fn test_add_from_impl_more_than_one_element_in_tuple() { | ||
158 | check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One(u32, String) }"); | ||
159 | } | ||
160 | |||
161 | #[test] | ||
162 | fn test_add_from_impl_struct_variant() { | ||
163 | check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One { x: u32 } }"); | ||
164 | } | ||
165 | |||
166 | #[test] | ||
167 | fn test_add_from_impl_already_exists() { | ||
168 | check_assist_not_applicable( | ||
169 | add_from_impl_for_enum, | ||
170 | r#"enum A { <|>One(u32), } | ||
171 | |||
172 | impl From<u32> for A { | ||
173 | fn from(v: u32) -> Self { | ||
174 | A::One(v) | ||
175 | } | ||
176 | } | ||
177 | |||
178 | pub trait From<T> { | ||
179 | fn from(T) -> Self; | ||
180 | }"#, | ||
181 | ); | ||
182 | } | ||
183 | |||
184 | #[test] | ||
185 | fn test_add_from_impl_different_variant_impl_exists() { | ||
186 | check_assist( | ||
187 | add_from_impl_for_enum, | ||
188 | r#"enum A { <|>One(u32), Two(String), } | ||
189 | |||
190 | impl From<String> for A { | ||
191 | fn from(v: String) -> Self { | ||
192 | A::Two(v) | ||
193 | } | ||
194 | } | ||
195 | |||
196 | pub trait From<T> { | ||
197 | fn from(T) -> Self; | ||
198 | }"#, | ||
199 | r#"enum A { One(u32), Two(String), } | ||
200 | |||
201 | <|>impl From<u32> for A { | ||
202 | fn from(v: u32) -> Self { | ||
203 | A::One(v) | ||
204 | } | ||
205 | } | ||
206 | |||
207 | impl From<String> for A { | ||
208 | fn from(v: String) -> Self { | ||
209 | A::Two(v) | ||
210 | } | ||
211 | } | ||
212 | |||
213 | pub trait From<T> { | ||
214 | fn from(T) -> Self; | ||
215 | }"#, | ||
216 | ); | ||
217 | } | ||
218 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index fa1f3dd26..6b4c56dcd 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -122,6 +122,7 @@ mod handlers { | |||
122 | mod replace_qualified_name_with_use; | 122 | mod replace_qualified_name_with_use; |
123 | mod replace_unwrap_with_match; | 123 | mod replace_unwrap_with_match; |
124 | mod split_import; | 124 | mod split_import; |
125 | mod add_from_impl_for_enum; | ||
125 | 126 | ||
126 | pub(crate) fn all() -> &'static [AssistHandler] { | 127 | pub(crate) fn all() -> &'static [AssistHandler] { |
127 | &[ | 128 | &[ |
@@ -159,6 +160,7 @@ mod handlers { | |||
159 | replace_qualified_name_with_use::replace_qualified_name_with_use, | 160 | replace_qualified_name_with_use::replace_qualified_name_with_use, |
160 | replace_unwrap_with_match::replace_unwrap_with_match, | 161 | replace_unwrap_with_match::replace_unwrap_with_match, |
161 | split_import::split_import, | 162 | split_import::split_import, |
163 | add_from_impl_for_enum::add_from_impl_for_enum, | ||
162 | ] | 164 | ] |
163 | } | 165 | } |
164 | } | 166 | } |