]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
7709: Updated the implementation.
[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, TextRange,
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     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
77 fn 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
92 fn 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)]
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 generate_is_empty() {
162         check_assist(
163             generate_is_empty_from_len,
164             r#"
165 struct MyStruct { data: Vec<String> }
166
167 impl MyStruct {
168     p$0ub fn len(&self) -> usize {
169         self.data.len()
170     }
171 }
172 "#,
173             r#"
174 struct MyStruct { data: Vec<String> }
175
176 impl 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#"
194 struct MyStruct { data: Vec<String> }
195
196 impl 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#"
211 struct MyStruct { data: Vec<String> }
212
213 impl 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#"
239 struct MyStruct { data: Vec<String> }
240
241 impl MyStruct {
242     p$0ub fn len(&self) -> usize {
243         self.data.len()
244     }
245 }
246
247 impl MyStruct {
248     pub fn is_empty(&self) -> bool {
249         self.len() == 0
250     }
251 }
252 "#,
253         );
254     }
255 }