aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers')
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs255
1 files changed, 255 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..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}