diff options
Diffstat (limited to 'crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs')
-rw-r--r-- | crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs new file mode 100644 index 000000000..e781be61e --- /dev/null +++ b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs | |||
@@ -0,0 +1,200 @@ | |||
1 | use ra_ide_db::RootDatabase; | ||
2 | use ra_syntax::ast::{self, AstNode, NameOwner}; | ||
3 | use test_utils::mark; | ||
4 | |||
5 | use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists}; | ||
6 | |||
7 | // Assist: generate_from_impl_for_enum | ||
8 | // | ||
9 | // Adds a From impl for an enum variant with one tuple field. | ||
10 | // | ||
11 | // ``` | ||
12 | // enum A { <|>One(u32) } | ||
13 | // ``` | ||
14 | // -> | ||
15 | // ``` | ||
16 | // enum A { One(u32) } | ||
17 | // | ||
18 | // impl From<u32> for A { | ||
19 | // fn from(v: u32) -> Self { | ||
20 | // A::One(v) | ||
21 | // } | ||
22 | // } | ||
23 | // ``` | ||
24 | pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
25 | let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; | ||
26 | let variant_name = variant.name()?; | ||
27 | let enum_name = variant.parent_enum().name()?; | ||
28 | let field_list = match variant.kind() { | ||
29 | ast::StructKind::Tuple(field_list) => field_list, | ||
30 | _ => return None, | ||
31 | }; | ||
32 | if field_list.fields().count() != 1 { | ||
33 | return None; | ||
34 | } | ||
35 | let field_type = field_list.fields().next()?.type_ref()?; | ||
36 | let path = match field_type { | ||
37 | ast::TypeRef::PathType(it) => it, | ||
38 | _ => return None, | ||
39 | }; | ||
40 | |||
41 | if existing_from_impl(&ctx.sema, &variant).is_some() { | ||
42 | mark::hit!(test_add_from_impl_already_exists); | ||
43 | return None; | ||
44 | } | ||
45 | |||
46 | let target = variant.syntax().text_range(); | ||
47 | acc.add( | ||
48 | AssistId("generate_from_impl_for_enum", AssistKind::Refactor), | ||
49 | "Generate `From` impl for this enum variant", | ||
50 | target, | ||
51 | |edit| { | ||
52 | let start_offset = variant.parent_enum().syntax().text_range().end(); | ||
53 | let buf = format!( | ||
54 | r#" | ||
55 | |||
56 | impl From<{0}> for {1} {{ | ||
57 | fn from(v: {0}) -> Self {{ | ||
58 | {1}::{2}(v) | ||
59 | }} | ||
60 | }}"#, | ||
61 | path.syntax(), | ||
62 | enum_name, | ||
63 | variant_name | ||
64 | ); | ||
65 | edit.insert(start_offset, buf); | ||
66 | }, | ||
67 | ) | ||
68 | } | ||
69 | |||
70 | fn existing_from_impl( | ||
71 | sema: &'_ hir::Semantics<'_, RootDatabase>, | ||
72 | variant: &ast::EnumVariant, | ||
73 | ) -> Option<()> { | ||
74 | let variant = sema.to_def(variant)?; | ||
75 | let enum_ = variant.parent_enum(sema.db); | ||
76 | let krate = enum_.module(sema.db).krate(); | ||
77 | |||
78 | let from_trait = FamousDefs(sema, krate).core_convert_From()?; | ||
79 | |||
80 | let enum_type = enum_.ty(sema.db); | ||
81 | |||
82 | let wrapped_type = variant.fields(sema.db).get(0)?.signature_ty(sema.db); | ||
83 | |||
84 | if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) { | ||
85 | Some(()) | ||
86 | } else { | ||
87 | None | ||
88 | } | ||
89 | } | ||
90 | |||
91 | #[cfg(test)] | ||
92 | mod tests { | ||
93 | use test_utils::mark; | ||
94 | |||
95 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
96 | |||
97 | use super::*; | ||
98 | |||
99 | #[test] | ||
100 | fn test_generate_from_impl_for_enum() { | ||
101 | check_assist( | ||
102 | generate_from_impl_for_enum, | ||
103 | "enum A { <|>One(u32) }", | ||
104 | r#"enum A { One(u32) } | ||
105 | |||
106 | impl From<u32> for A { | ||
107 | fn from(v: u32) -> Self { | ||
108 | A::One(v) | ||
109 | } | ||
110 | }"#, | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | #[test] | ||
115 | fn test_generate_from_impl_for_enum_complicated_path() { | ||
116 | check_assist( | ||
117 | generate_from_impl_for_enum, | ||
118 | r#"enum A { <|>One(foo::bar::baz::Boo) }"#, | ||
119 | r#"enum A { One(foo::bar::baz::Boo) } | ||
120 | |||
121 | impl From<foo::bar::baz::Boo> for A { | ||
122 | fn from(v: foo::bar::baz::Boo) -> Self { | ||
123 | A::One(v) | ||
124 | } | ||
125 | }"#, | ||
126 | ); | ||
127 | } | ||
128 | |||
129 | fn check_not_applicable(ra_fixture: &str) { | ||
130 | let fixture = | ||
131 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | ||
132 | check_assist_not_applicable(generate_from_impl_for_enum, &fixture) | ||
133 | } | ||
134 | |||
135 | #[test] | ||
136 | fn test_add_from_impl_no_element() { | ||
137 | check_not_applicable("enum A { <|>One }"); | ||
138 | } | ||
139 | |||
140 | #[test] | ||
141 | fn test_add_from_impl_more_than_one_element_in_tuple() { | ||
142 | check_not_applicable("enum A { <|>One(u32, String) }"); | ||
143 | } | ||
144 | |||
145 | #[test] | ||
146 | fn test_add_from_impl_struct_variant() { | ||
147 | check_not_applicable("enum A { <|>One { x: u32 } }"); | ||
148 | } | ||
149 | |||
150 | #[test] | ||
151 | fn test_add_from_impl_already_exists() { | ||
152 | mark::check!(test_add_from_impl_already_exists); | ||
153 | check_not_applicable( | ||
154 | r#" | ||
155 | enum A { <|>One(u32), } | ||
156 | |||
157 | impl From<u32> for A { | ||
158 | fn from(v: u32) -> Self { | ||
159 | A::One(v) | ||
160 | } | ||
161 | } | ||
162 | "#, | ||
163 | ); | ||
164 | } | ||
165 | |||
166 | #[test] | ||
167 | fn test_add_from_impl_different_variant_impl_exists() { | ||
168 | check_assist( | ||
169 | generate_from_impl_for_enum, | ||
170 | r#"enum A { <|>One(u32), Two(String), } | ||
171 | |||
172 | impl From<String> for A { | ||
173 | fn from(v: String) -> Self { | ||
174 | A::Two(v) | ||
175 | } | ||
176 | } | ||
177 | |||
178 | pub trait From<T> { | ||
179 | fn from(T) -> Self; | ||
180 | }"#, | ||
181 | r#"enum A { One(u32), Two(String), } | ||
182 | |||
183 | impl From<u32> for A { | ||
184 | fn from(v: u32) -> Self { | ||
185 | A::One(v) | ||
186 | } | ||
187 | } | ||
188 | |||
189 | impl From<String> for A { | ||
190 | fn from(v: String) -> Self { | ||
191 | A::Two(v) | ||
192 | } | ||
193 | } | ||
194 | |||
195 | pub trait From<T> { | ||
196 | fn from(T) -> Self; | ||
197 | }"#, | ||
198 | ); | ||
199 | } | ||
200 | } | ||