]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
Merge #11481
[rust.git] / crates / ide_assists / src / handlers / generate_is_empty_from_len.rs
1 use hir::{known, HasSource, Name};
2 use syntax::{
3     ast::{self, HasName},
4     AstNode,
5 };
6
7 use 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 // ```
39 pub(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
84 fn 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.self_ty(db);
95     let traits_in_scope = scope.visible_traits();
96     ty.iterate_method_candidates(db, krate, &traits_in_scope, None, Some(fn_name), |_, func| {
97         Some(func)
98     })
99 }
100
101 #[cfg(test)]
102 mod tests {
103     use crate::tests::{check_assist, check_assist_not_applicable};
104
105     use super::*;
106
107     #[test]
108     fn len_function_not_present() {
109         cov_mark::check!(len_function_not_present);
110         check_assist_not_applicable(
111             generate_is_empty_from_len,
112             r#"
113 struct MyStruct { data: Vec<String> }
114
115 impl MyStruct {
116     p$0ub fn test(&self) -> usize {
117             self.data.len()
118         }
119     }
120 "#,
121         );
122     }
123
124     #[test]
125     fn len_function_with_parameters() {
126         cov_mark::check!(len_function_with_parameters);
127         check_assist_not_applicable(
128             generate_is_empty_from_len,
129             r#"
130 struct MyStruct { data: Vec<String> }
131
132 impl MyStruct {
133     p$0ub fn len(&self, _i: bool) -> usize {
134         self.data.len()
135     }
136 }
137 "#,
138         );
139     }
140
141     #[test]
142     fn is_empty_already_implemented() {
143         cov_mark::check!(is_empty_already_implemented);
144         check_assist_not_applicable(
145             generate_is_empty_from_len,
146             r#"
147 struct MyStruct { data: Vec<String> }
148
149 impl MyStruct {
150     p$0ub fn len(&self) -> usize {
151         self.data.len()
152     }
153
154     pub fn is_empty(&self) -> bool {
155         self.len() == 0
156     }
157 }
158 "#,
159         );
160     }
161
162     #[test]
163     fn len_fn_different_return_type() {
164         cov_mark::check!(len_fn_different_return_type);
165         check_assist_not_applicable(
166             generate_is_empty_from_len,
167             r#"
168 struct MyStruct { data: Vec<String> }
169
170 impl MyStruct {
171     p$0ub fn len(&self) -> u32 {
172         self.data.len()
173     }
174 }
175 "#,
176         );
177     }
178
179     #[test]
180     fn generate_is_empty() {
181         check_assist(
182             generate_is_empty_from_len,
183             r#"
184 struct MyStruct { data: Vec<String> }
185
186 impl MyStruct {
187     p$0ub fn len(&self) -> usize {
188         self.data.len()
189     }
190 }
191 "#,
192             r#"
193 struct MyStruct { data: Vec<String> }
194
195 impl MyStruct {
196     pub fn len(&self) -> usize {
197         self.data.len()
198     }
199
200     pub fn is_empty(&self) -> bool {
201         self.len() == 0
202     }
203 }
204 "#,
205         );
206     }
207
208     #[test]
209     fn multiple_functions_in_impl() {
210         check_assist(
211             generate_is_empty_from_len,
212             r#"
213 struct MyStruct { data: Vec<String> }
214
215 impl MyStruct {
216     pub fn new() -> Self {
217         Self { data: 0 }
218     }
219
220     p$0ub fn len(&self) -> usize {
221         self.data.len()
222     }
223
224     pub fn work(&self) -> Option<usize> {
225
226     }
227 }
228 "#,
229             r#"
230 struct MyStruct { data: Vec<String> }
231
232 impl MyStruct {
233     pub fn new() -> Self {
234         Self { data: 0 }
235     }
236
237     pub fn len(&self) -> usize {
238         self.data.len()
239     }
240
241     pub fn is_empty(&self) -> bool {
242         self.len() == 0
243     }
244
245     pub fn work(&self) -> Option<usize> {
246
247     }
248 }
249 "#,
250         );
251     }
252
253     #[test]
254     fn multiple_impls() {
255         check_assist_not_applicable(
256             generate_is_empty_from_len,
257             r#"
258 struct MyStruct { data: Vec<String> }
259
260 impl MyStruct {
261     p$0ub fn len(&self) -> usize {
262         self.data.len()
263     }
264 }
265
266 impl MyStruct {
267     pub fn is_empty(&self) -> bool {
268         self.len() == 0
269     }
270 }
271 "#,
272         );
273     }
274 }