aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers/generate_is_empty_from_len.rs')
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs272
1 files changed, 272 insertions, 0 deletions
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..b8834d283
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -0,0 +1,272 @@
1use hir::{known, HasSource, Name};
2use syntax::{
3 ast::{self, NameOwner},
4 AstNode,
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 let len_fn = get_impl_method(ctx, &impl_, &known::len)?;
55 if !len_fn.ret_type(ctx.sema.db).is_usize() {
56 cov_mark::hit!(len_fn_different_return_type);
57 return None;
58 }
59
60 if get_impl_method(ctx, &impl_, &known::is_empty).is_some() {
61 cov_mark::hit!(is_empty_already_implemented);
62 return None;
63 }
64
65 let node = len_fn.source(ctx.sema.db)?;
66 let range = node.syntax().value.text_range();
67
68 acc.add(
69 AssistId("generate_is_empty_from_len", AssistKind::Generate),
70 "Generate a is_empty impl from a len function",
71 range,
72 |builder| {
73 let code = r#"
74
75 pub fn is_empty(&self) -> bool {
76 self.len() == 0
77 }"#
78 .to_string();
79 builder.insert(range.end(), code)
80 },
81 )
82}
83
84fn get_impl_method(
85 ctx: &AssistContext,
86 impl_: &ast::Impl,
87 fn_name: &Name,
88) -> Option<hir::Function> {
89 let db = ctx.sema.db;
90 let impl_def: hir::Impl = ctx.sema.to_def(impl_)?;
91
92 let scope = ctx.sema.scope(impl_.syntax());
93 let krate = impl_def.module(db).krate();
94 let ty = impl_def.target_ty(db);
95 let traits_in_scope = scope.traits_in_scope();
96 ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func))
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 len_fn_different_return_type() {
162 cov_mark::check!(len_fn_different_return_type);
163 check_assist_not_applicable(
164 generate_is_empty_from_len,
165 r#"
166struct MyStruct { data: Vec<String> }
167
168impl MyStruct {
169 p$0ub fn len(&self) -> u32 {
170 self.data.len()
171 }
172}
173"#,
174 );
175 }
176
177 #[test]
178 fn generate_is_empty() {
179 check_assist(
180 generate_is_empty_from_len,
181 r#"
182struct MyStruct { data: Vec<String> }
183
184impl MyStruct {
185 p$0ub fn len(&self) -> usize {
186 self.data.len()
187 }
188}
189"#,
190 r#"
191struct MyStruct { data: Vec<String> }
192
193impl MyStruct {
194 pub fn len(&self) -> usize {
195 self.data.len()
196 }
197
198 pub fn is_empty(&self) -> bool {
199 self.len() == 0
200 }
201}
202"#,
203 );
204 }
205
206 #[test]
207 fn multiple_functions_in_impl() {
208 check_assist(
209 generate_is_empty_from_len,
210 r#"
211struct MyStruct { data: Vec<String> }
212
213impl MyStruct {
214 pub fn new() -> Self {
215 Self { data: 0 }
216 }
217
218 p$0ub fn len(&self) -> usize {
219 self.data.len()
220 }
221
222 pub fn work(&self) -> Option<usize> {
223
224 }
225}
226"#,
227 r#"
228struct MyStruct { data: Vec<String> }
229
230impl MyStruct {
231 pub fn new() -> Self {
232 Self { data: 0 }
233 }
234
235 pub fn len(&self) -> usize {
236 self.data.len()
237 }
238
239 pub fn is_empty(&self) -> bool {
240 self.len() == 0
241 }
242
243 pub fn work(&self) -> Option<usize> {
244
245 }
246}
247"#,
248 );
249 }
250
251 #[test]
252 fn multiple_impls() {
253 check_assist_not_applicable(
254 generate_is_empty_from_len,
255 r#"
256struct MyStruct { data: Vec<String> }
257
258impl MyStruct {
259 p$0ub fn len(&self) -> usize {
260 self.data.len()
261 }
262}
263
264impl MyStruct {
265 pub fn is_empty(&self) -> bool {
266 self.len() == 0
267 }
268}
269"#,
270 );
271 }
272}