aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs
diff options
context:
space:
mode:
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.rs200
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 @@
1use ra_ide_db::RootDatabase;
2use ra_syntax::ast::{self, AstNode, NameOwner};
3use test_utils::mark;
4
5use 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// ```
24pub(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
56impl 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
70fn 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)]
92mod 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
106impl 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
121impl 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#"
155enum A { <|>One(u32), }
156
157impl 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
172impl From<String> for A {
173 fn from(v: String) -> Self {
174 A::Two(v)
175 }
176}
177
178pub trait From<T> {
179 fn from(T) -> Self;
180}"#,
181 r#"enum A { One(u32), Two(String), }
182
183impl From<u32> for A {
184 fn from(v: u32) -> Self {
185 A::One(v)
186 }
187}
188
189impl From<String> for A {
190 fn from(v: String) -> Self {
191 A::Two(v)
192 }
193}
194
195pub trait From<T> {
196 fn from(T) -> Self;
197}"#,
198 );
199 }
200}