aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs206
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_hir/src/code_model.rs20
-rw-r--r--crates/ra_syntax/src/ast/make.rs3
4 files changed, 230 insertions, 1 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..864373aa5
--- /dev/null
+++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
@@ -0,0 +1,206 @@
1use ra_syntax::{
2 ast::{self, AstNode, NameOwner},
3 TextUnit,
4};
5use stdx::format_to;
6
7use crate::{Assist, AssistCtx, AssistId};
8use ra_ide_db::RootDatabase;
9
10// Assist add_from_impl_for_enum
11//
12// Adds a From impl for an enum variant with one tuple field
13//
14// ```
15// enum A { <|>One(u32) }
16// ```
17// ->
18// ```
19// enum A { One(u32) }
20//
21// impl From<u32> for A {
22// fn from(v: u32) -> Self {
23// A::One(v)
24// }
25// }
26// ```
27pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> {
28 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
29 let variant_name = variant.name()?;
30 let enum_name = variant.parent_enum().name()?;
31 let field_list = match variant.kind() {
32 ast::StructKind::Tuple(field_list) => field_list,
33 _ => return None,
34 };
35 if field_list.fields().count() != 1 {
36 return None;
37 }
38 let field_type = field_list.fields().next()?.type_ref()?;
39 let path = match field_type {
40 ast::TypeRef::PathType(p) => p,
41 _ => return None,
42 };
43
44 if already_has_from_impl(ctx.sema, &variant) {
45 return None;
46 }
47
48 ctx.add_assist(
49 AssistId("add_from_impl_for_enum"),
50 "Add From impl for this enum variant",
51 |edit| {
52 let start_offset = variant.parent_enum().syntax().text_range().end();
53 let mut buf = String::new();
54 format_to!(
55 buf,
56 r#"
57
58impl From<{0}> for {1} {{
59 fn from(v: {0}) -> Self {{
60 {1}::{2}(v)
61 }}
62}}"#,
63 path.syntax(),
64 enum_name,
65 variant_name
66 );
67 edit.insert(start_offset, buf);
68 edit.set_cursor(start_offset + TextUnit::of_str("\n\n"));
69 },
70 )
71}
72
73fn already_has_from_impl(
74 sema: &'_ hir::Semantics<'_, RootDatabase>,
75 variant: &ast::EnumVariant,
76) -> bool {
77 let scope = sema.scope(&variant.syntax());
78
79 let from_path = ast::make::path_from_text("From");
80 let from_hir_path = match hir::Path::from_ast(from_path) {
81 Some(p) => p,
82 None => return false,
83 };
84 let from_trait = match scope.resolve_hir_path(&from_hir_path) {
85 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(t))) => t,
86 _ => return false,
87 };
88
89 let e: hir::Enum = match sema.to_def(&variant.parent_enum()) {
90 Some(e) => e,
91 None => return false,
92 };
93 let e_ty = e.ty(sema.db);
94
95 let hir_enum_var: hir::EnumVariant = match sema.to_def(variant) {
96 Some(ev) => ev,
97 None => return false,
98 };
99 let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db);
100
101 e_ty.impls_trait(sema.db, from_trait, &[var_ty.clone()])
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::helpers::{check_assist, check_assist_not_applicable};
108
109 #[test]
110 fn test_add_from_impl_for_enum() {
111 check_assist(
112 add_from_impl_for_enum,
113 "enum A { <|>One(u32) }",
114 r#"enum A { One(u32) }
115
116<|>impl From<u32> for A {
117 fn from(v: u32) -> Self {
118 A::One(v)
119 }
120}"#,
121 );
122 }
123
124 #[test]
125 fn test_add_from_impl_for_enum_complicated_path() {
126 check_assist(
127 add_from_impl_for_enum,
128 "enum A { <|>One(foo::bar::baz::Boo) }",
129 r#"enum A { One(foo::bar::baz::Boo) }
130
131<|>impl From<foo::bar::baz::Boo> for A {
132 fn from(v: foo::bar::baz::Boo) -> Self {
133 A::One(v)
134 }
135}"#,
136 );
137 }
138
139 #[test]
140 fn test_add_from_impl_no_element() {
141 check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One }");
142 }
143
144 #[test]
145 fn test_add_from_impl_more_than_one_element_in_tuple() {
146 check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One(u32, String) }");
147 }
148
149 #[test]
150 fn test_add_from_impl_struct_variant() {
151 check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One { x: u32 } }");
152 }
153
154 #[test]
155 fn test_add_from_impl_already_exists() {
156 check_assist_not_applicable(
157 add_from_impl_for_enum,
158 r#"enum A { <|>One(u32), }
159
160impl From<u32> for A {
161 fn from(v: u32) -> Self {
162 A::One(v)
163 }
164}
165
166pub trait From<T> {
167 fn from(T) -> Self;
168}"#,
169 );
170 }
171
172 #[test]
173 fn test_add_from_impl_different_variant_impl_exists() {
174 check_assist(
175 add_from_impl_for_enum,
176 r#"enum A { <|>One(u32), Two(String), }
177
178impl From<String> for A {
179 fn from(v: String) -> Self {
180 A::Two(v)
181 }
182}
183
184pub trait From<T> {
185 fn from(T) -> Self;
186}"#,
187 r#"enum A { One(u32), Two(String), }
188
189<|>impl From<u32> for A {
190 fn from(v: u32) -> Self {
191 A::One(v)
192 }
193}
194
195impl From<String> for A {
196 fn from(v: String) -> Self {
197 A::Two(v)
198 }
199}
200
201pub trait From<T> {
202 fn from(T) -> Self;
203}"#,
204 );
205 }
206}
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}
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index cd2a8fc62..c6f3bdb8e 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1084,6 +1084,26 @@ impl Type {
1084 ) 1084 )
1085 } 1085 }
1086 1086
1087 pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
1088 let trait_ref = hir_ty::TraitRef {
1089 trait_: trait_.id,
1090 substs: Substs::build_for_def(db, trait_.id)
1091 .push(self.ty.value.clone())
1092 .fill(args.iter().map(|t| t.ty.value.clone()))
1093 .build(),
1094 };
1095
1096 let goal = Canonical {
1097 value: hir_ty::InEnvironment::new(
1098 self.ty.environment.clone(),
1099 hir_ty::Obligation::Trait(trait_ref),
1100 ),
1101 num_vars: 0,
1102 };
1103
1104 db.trait_solve(self.krate, goal).is_some()
1105 }
1106
1087 // FIXME: this method is broken, as it doesn't take closures into account. 1107 // FIXME: this method is broken, as it doesn't take closures into account.
1088 pub fn as_callable(&self) -> Option<CallableDef> { 1108 pub fn as_callable(&self) -> Option<CallableDef> {
1089 Some(self.ty.value.as_callable()?.0) 1109 Some(self.ty.value.as_callable()?.0)
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 0c908573d..c49cf9a3b 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -22,7 +22,8 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
22pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { 22pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
23 path_from_text(&format!("{}::{}", qual, segment)) 23 path_from_text(&format!("{}::{}", qual, segment))
24} 24}
25fn path_from_text(text: &str) -> ast::Path { 25
26pub fn path_from_text(text: &str) -> ast::Path {
26 ast_from_text(text) 27 ast_from_text(text)
27} 28}
28 29