aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir_expand/src/name.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs255
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs29
4 files changed, 288 insertions, 0 deletions
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index e833e032c..43de9edd6 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -191,6 +191,8 @@ pub mod known {
191 filter_map, 191 filter_map,
192 next, 192 next,
193 iter_mut, 193 iter_mut,
194 len,
195 is_empty,
194 // Builtin macros 196 // Builtin macros
195 file, 197 file,
196 column, 198 column,
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
new file mode 100644
index 000000000..aa7072f25
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -0,0 +1,255 @@
1use hir::{known, HasSource, Name};
2use syntax::{
3 ast::{self, NameOwner},
4 AstNode, TextRange,
5};
6
7use crate::{
8 assist_context::{AssistContext, Assists},
9 AssistId, AssistKind,
10};
11
12// Assist: generate_is_empty_from_len
13//
14// Generates is_empty implementation from the len method.
15//
16// ```
17// struct MyStruct { data: Vec<String> }
18//
19// impl MyStruct {
20// p$0ub fn len(&self) -> usize {
21// self.data.len()
22// }
23// }
24// ```
25// ->
26// ```
27// struct MyStruct { data: Vec<String> }
28//
29// impl MyStruct {
30// pub fn len(&self) -> usize {
31// self.data.len()
32// }
33//
34// pub fn is_empty(&self) -> bool {
35// self.len() == 0
36// }
37// }
38// ```
39pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
41 let fn_name = fn_node.name()?;
42
43 if fn_name.text() != "len" {
44 cov_mark::hit!(len_function_not_present);
45 return None;
46 }
47
48 if fn_node.param_list()?.params().next().is_some() {
49 cov_mark::hit!(len_function_with_parameters);
50 return None;
51 }
52
53 let impl_ = fn_node.syntax().ancestors().find_map(ast::Impl::cast)?;
54 if get_impl_method(ctx, &impl_, &known::is_empty).is_some() {
55 cov_mark::hit!(is_empty_already_implemented);
56 return None;
57 }
58
59 let range = get_text_range_of_len_function(ctx, &impl_)?;
60
61 acc.add(
62 AssistId("generate_is_empty_from_len", AssistKind::Generate),
63 "Generate a is_empty impl from a len function",
64 range,
65 |builder| {
66 let code = r#"
67
68 pub fn is_empty(&self) -> bool {
69 self.len() == 0
70 }"#
71 .to_string();
72 builder.insert(range.end(), code)
73 },
74 )
75}
76
77fn get_impl_method(
78 ctx: &AssistContext,
79 impl_: &ast::Impl,
80 fn_name: &Name,
81) -> Option<hir::Function> {
82 let db = ctx.sema.db;
83 let impl_def: hir::Impl = ctx.sema.to_def(impl_)?;
84
85 let scope = ctx.sema.scope(impl_.syntax());
86 let krate = impl_def.module(db).krate();
87 let ty = impl_def.target_ty(db);
88 let traits_in_scope = scope.traits_in_scope();
89 ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func))
90}
91
92fn get_text_range_of_len_function(ctx: &AssistContext, impl_: &ast::Impl) -> Option<TextRange> {
93 let db = ctx.sema.db;
94 let func = get_impl_method(ctx, impl_, &known::len)?;
95 let node = func.source(db)?;
96 Some(node.syntax().value.text_range())
97}
98
99#[cfg(test)]
100mod tests {
101 use crate::tests::{check_assist, check_assist_not_applicable};
102
103 use super::*;
104
105 #[test]
106 fn len_function_not_present() {
107 cov_mark::check!(len_function_not_present);
108 check_assist_not_applicable(
109 generate_is_empty_from_len,
110 r#"
111struct MyStruct { data: Vec<String> }
112
113impl MyStruct {
114 p$0ub fn test(&self) -> usize {
115 self.data.len()
116 }
117 }
118"#,
119 );
120 }
121
122 #[test]
123 fn len_function_with_parameters() {
124 cov_mark::check!(len_function_with_parameters);
125 check_assist_not_applicable(
126 generate_is_empty_from_len,
127 r#"
128struct MyStruct { data: Vec<String> }
129
130impl MyStruct {
131 p$0ub fn len(&self, _i: bool) -> usize {
132 self.data.len()
133 }
134}
135"#,
136 );
137 }
138
139 #[test]
140 fn is_empty_already_implemented() {
141 cov_mark::check!(is_empty_already_implemented);
142 check_assist_not_applicable(
143 generate_is_empty_from_len,
144 r#"
145struct MyStruct { data: Vec<String> }
146
147impl MyStruct {
148 p$0ub fn len(&self) -> usize {
149 self.data.len()
150 }
151
152 pub fn is_empty(&self) -> bool {
153 self.len() == 0
154 }
155}
156"#,
157 );
158 }
159
160 #[test]
161 fn generate_is_empty() {
162 check_assist(
163 generate_is_empty_from_len,
164 r#"
165struct MyStruct { data: Vec<String> }
166
167impl MyStruct {
168 p$0ub fn len(&self) -> usize {
169 self.data.len()
170 }
171}
172"#,
173 r#"
174struct MyStruct { data: Vec<String> }
175
176impl MyStruct {
177 pub fn len(&self) -> usize {
178 self.data.len()
179 }
180
181 pub fn is_empty(&self) -> bool {
182 self.len() == 0
183 }
184}
185"#,
186 );
187 }
188
189 #[test]
190 fn multiple_functions_in_impl() {
191 check_assist(
192 generate_is_empty_from_len,
193 r#"
194struct MyStruct { data: Vec<String> }
195
196impl MyStruct {
197 pub fn new() -> Self {
198 Self { data: 0 }
199 }
200
201 p$0ub fn len(&self) -> usize {
202 self.data.len()
203 }
204
205 pub fn work(&self) -> Option<usize> {
206
207 }
208}
209"#,
210 r#"
211struct MyStruct { data: Vec<String> }
212
213impl MyStruct {
214 pub fn new() -> Self {
215 Self { data: 0 }
216 }
217
218 pub fn len(&self) -> usize {
219 self.data.len()
220 }
221
222 pub fn is_empty(&self) -> bool {
223 self.len() == 0
224 }
225
226 pub fn work(&self) -> Option<usize> {
227
228 }
229}
230"#,
231 );
232 }
233
234 #[test]
235 fn multiple_impls() {
236 check_assist_not_applicable(
237 generate_is_empty_from_len,
238 r#"
239struct MyStruct { data: Vec<String> }
240
241impl MyStruct {
242 p$0ub fn len(&self) -> usize {
243 self.data.len()
244 }
245}
246
247impl MyStruct {
248 pub fn is_empty(&self) -> bool {
249 self.len() == 0
250 }
251}
252"#,
253 );
254 }
255}
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index f1aab74d4..8c068a6c0 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -129,6 +129,7 @@ mod handlers {
129 mod flip_trait_bound; 129 mod flip_trait_bound;
130 mod generate_default_from_enum_variant; 130 mod generate_default_from_enum_variant;
131 mod generate_default_from_new; 131 mod generate_default_from_new;
132 mod generate_is_empty_from_len;
132 mod generate_derive; 133 mod generate_derive;
133 mod generate_enum_is_method; 134 mod generate_enum_is_method;
134 mod generate_enum_projection_method; 135 mod generate_enum_projection_method;
@@ -193,6 +194,7 @@ mod handlers {
193 flip_trait_bound::flip_trait_bound, 194 flip_trait_bound::flip_trait_bound,
194 generate_default_from_enum_variant::generate_default_from_enum_variant, 195 generate_default_from_enum_variant::generate_default_from_enum_variant,
195 generate_default_from_new::generate_default_from_new, 196 generate_default_from_new::generate_default_from_new,
197 generate_is_empty_from_len::generate_is_empty_from_len,
196 generate_derive::generate_derive, 198 generate_derive::generate_derive,
197 generate_enum_is_method::generate_enum_is_method, 199 generate_enum_is_method::generate_enum_is_method,
198 generate_enum_projection_method::generate_enum_as_method, 200 generate_enum_projection_method::generate_enum_as_method,
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 3f77edd8d..736027ff0 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -722,6 +722,35 @@ impl<T: Clone> Ctx<T> {
722} 722}
723 723
724#[test] 724#[test]
725fn doctest_generate_is_empty_from_len() {
726 check_doc_test(
727 "generate_is_empty_from_len",
728 r#####"
729struct MyStruct { data: Vec<String> }
730
731impl MyStruct {
732 p$0ub fn len(&self) -> usize {
733 self.data.len()
734 }
735}
736"#####,
737 r#####"
738struct MyStruct { data: Vec<String> }
739
740impl MyStruct {
741 pub fn len(&self) -> usize {
742 self.data.len()
743 }
744
745 pub fn is_empty(&self) -> bool {
746 self.len() == 0
747 }
748}
749"#####,
750 )
751}
752
753#[test]
725fn doctest_generate_new() { 754fn doctest_generate_new() {
726 check_doc_test( 755 check_doc_test(
727 "generate_new", 756 "generate_new",