]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
Rollup merge of #98391 - joboet:sgx_parker, r=m-ou-se
[rust.git] / src / tools / rust-analyzer / 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 //     #[must_use]
21 //     p$0ub fn len(&self) -> usize {
22 //         self.data.len()
23 //     }
24 // }
25 // ```
26 // ->
27 // ```
28 // struct MyStruct { data: Vec<String> }
29 //
30 // impl MyStruct {
31 //     #[must_use]
32 //     pub fn len(&self) -> usize {
33 //         self.data.len()
34 //     }
35 //
36 //     #[must_use]
37 //     pub fn is_empty(&self) -> bool {
38 //         self.len() == 0
39 //     }
40 // }
41 // ```
42 pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
43     let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
44     let fn_name = fn_node.name()?;
45
46     if fn_name.text() != "len" {
47         cov_mark::hit!(len_function_not_present);
48         return None;
49     }
50
51     if fn_node.param_list()?.params().next().is_some() {
52         cov_mark::hit!(len_function_with_parameters);
53         return None;
54     }
55
56     let impl_ = fn_node.syntax().ancestors().find_map(ast::Impl::cast)?;
57     let len_fn = get_impl_method(ctx, &impl_, &known::len)?;
58     if !len_fn.ret_type(ctx.sema.db).is_usize() {
59         cov_mark::hit!(len_fn_different_return_type);
60         return None;
61     }
62
63     if get_impl_method(ctx, &impl_, &known::is_empty).is_some() {
64         cov_mark::hit!(is_empty_already_implemented);
65         return None;
66     }
67
68     let node = len_fn.source(ctx.sema.db)?;
69     let range = node.syntax().value.text_range();
70
71     acc.add(
72         AssistId("generate_is_empty_from_len", AssistKind::Generate),
73         "Generate a is_empty impl from a len function",
74         range,
75         |builder| {
76             let code = r#"
77
78     #[must_use]
79     pub fn is_empty(&self) -> bool {
80         self.len() == 0
81     }"#
82             .to_string();
83             builder.insert(range.end(), code)
84         },
85     )
86 }
87
88 fn get_impl_method(
89     ctx: &AssistContext<'_>,
90     impl_: &ast::Impl,
91     fn_name: &Name,
92 ) -> Option<hir::Function> {
93     let db = ctx.sema.db;
94     let impl_def: hir::Impl = ctx.sema.to_def(impl_)?;
95
96     let scope = ctx.sema.scope(impl_.syntax())?;
97     let ty = impl_def.self_ty(db);
98     ty.iterate_method_candidates(
99         db,
100         &scope,
101         &scope.visible_traits().0,
102         None,
103         Some(fn_name),
104         |func| Some(func),
105     )
106 }
107
108 #[cfg(test)]
109 mod tests {
110     use crate::tests::{check_assist, check_assist_not_applicable};
111
112     use super::*;
113
114     #[test]
115     fn len_function_not_present() {
116         cov_mark::check!(len_function_not_present);
117         check_assist_not_applicable(
118             generate_is_empty_from_len,
119             r#"
120 struct MyStruct { data: Vec<String> }
121
122 impl MyStruct {
123     p$0ub fn test(&self) -> usize {
124             self.data.len()
125         }
126     }
127 "#,
128         );
129     }
130
131     #[test]
132     fn len_function_with_parameters() {
133         cov_mark::check!(len_function_with_parameters);
134         check_assist_not_applicable(
135             generate_is_empty_from_len,
136             r#"
137 struct MyStruct { data: Vec<String> }
138
139 impl MyStruct {
140     #[must_use]
141     p$0ub fn len(&self, _i: bool) -> usize {
142         self.data.len()
143     }
144 }
145 "#,
146         );
147     }
148
149     #[test]
150     fn is_empty_already_implemented() {
151         cov_mark::check!(is_empty_already_implemented);
152         check_assist_not_applicable(
153             generate_is_empty_from_len,
154             r#"
155 struct MyStruct { data: Vec<String> }
156
157 impl MyStruct {
158     #[must_use]
159     p$0ub fn len(&self) -> usize {
160         self.data.len()
161     }
162
163     #[must_use]
164     pub fn is_empty(&self) -> bool {
165         self.len() == 0
166     }
167 }
168 "#,
169         );
170     }
171
172     #[test]
173     fn len_fn_different_return_type() {
174         cov_mark::check!(len_fn_different_return_type);
175         check_assist_not_applicable(
176             generate_is_empty_from_len,
177             r#"
178 struct MyStruct { data: Vec<String> }
179
180 impl MyStruct {
181     #[must_use]
182     p$0ub fn len(&self) -> u32 {
183         self.data.len()
184     }
185 }
186 "#,
187         );
188     }
189
190     #[test]
191     fn generate_is_empty() {
192         check_assist(
193             generate_is_empty_from_len,
194             r#"
195 struct MyStruct { data: Vec<String> }
196
197 impl MyStruct {
198     #[must_use]
199     p$0ub fn len(&self) -> usize {
200         self.data.len()
201     }
202 }
203 "#,
204             r#"
205 struct MyStruct { data: Vec<String> }
206
207 impl MyStruct {
208     #[must_use]
209     pub fn len(&self) -> usize {
210         self.data.len()
211     }
212
213     #[must_use]
214     pub fn is_empty(&self) -> bool {
215         self.len() == 0
216     }
217 }
218 "#,
219         );
220     }
221
222     #[test]
223     fn multiple_functions_in_impl() {
224         check_assist(
225             generate_is_empty_from_len,
226             r#"
227 struct MyStruct { data: Vec<String> }
228
229 impl MyStruct {
230     #[must_use]
231     pub fn new() -> Self {
232         Self { data: 0 }
233     }
234
235     #[must_use]
236     p$0ub fn len(&self) -> usize {
237         self.data.len()
238     }
239
240     pub fn work(&self) -> Option<usize> {
241
242     }
243 }
244 "#,
245             r#"
246 struct MyStruct { data: Vec<String> }
247
248 impl MyStruct {
249     #[must_use]
250     pub fn new() -> Self {
251         Self { data: 0 }
252     }
253
254     #[must_use]
255     pub fn len(&self) -> usize {
256         self.data.len()
257     }
258
259     #[must_use]
260     pub fn is_empty(&self) -> bool {
261         self.len() == 0
262     }
263
264     pub fn work(&self) -> Option<usize> {
265
266     }
267 }
268 "#,
269         );
270     }
271
272     #[test]
273     fn multiple_impls() {
274         check_assist_not_applicable(
275             generate_is_empty_from_len,
276             r#"
277 struct MyStruct { data: Vec<String> }
278
279 impl MyStruct {
280     #[must_use]
281     p$0ub fn len(&self) -> usize {
282         self.data.len()
283     }
284 }
285
286 impl MyStruct {
287     #[must_use]
288     pub fn is_empty(&self) -> bool {
289         self.len() == 0
290     }
291 }
292 "#,
293         );
294     }
295 }