]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
Merge #7900 #8000
[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, NameOwner},
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.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)]
100 mod 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#"
111 struct MyStruct { data: Vec<String> }
112
113 impl 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#"
128 struct MyStruct { data: Vec<String> }
129
130 impl 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#"
145 struct MyStruct { data: Vec<String> }
146
147 impl 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#"
166 struct MyStruct { data: Vec<String> }
167
168 impl 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#"
182 struct MyStruct { data: Vec<String> }
183
184 impl MyStruct {
185     p$0ub fn len(&self) -> usize {
186         self.data.len()
187     }
188 }
189 "#,
190             r#"
191 struct MyStruct { data: Vec<String> }
192
193 impl 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#"
211 struct MyStruct { data: Vec<String> }
212
213 impl 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#"
228 struct MyStruct { data: Vec<String> }
229
230 impl 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#"
256 struct MyStruct { data: Vec<String> }
257
258 impl MyStruct {
259     p$0ub fn len(&self) -> usize {
260         self.data.len()
261     }
262 }
263
264 impl MyStruct {
265     pub fn is_empty(&self) -> bool {
266         self.len() == 0
267     }
268 }
269 "#,
270         );
271     }
272 }