]>
Commit | Line | Data |
---|---|---|
064997fb FG |
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); | |
353b0b11 | 98 | ty.iterate_method_candidates(db, &scope, None, Some(fn_name), |func| Some(func)) |
064997fb FG |
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 | #[must_use] | |
134 | p$0ub fn len(&self, _i: bool) -> usize { | |
135 | self.data.len() | |
136 | } | |
137 | } | |
138 | "#, | |
139 | ); | |
140 | } | |
141 | ||
142 | #[test] | |
143 | fn is_empty_already_implemented() { | |
144 | cov_mark::check!(is_empty_already_implemented); | |
145 | check_assist_not_applicable( | |
146 | generate_is_empty_from_len, | |
147 | r#" | |
148 | struct MyStruct { data: Vec<String> } | |
149 | ||
150 | impl MyStruct { | |
151 | #[must_use] | |
152 | p$0ub fn len(&self) -> usize { | |
153 | self.data.len() | |
154 | } | |
155 | ||
156 | #[must_use] | |
157 | pub fn is_empty(&self) -> bool { | |
158 | self.len() == 0 | |
159 | } | |
160 | } | |
161 | "#, | |
162 | ); | |
163 | } | |
164 | ||
165 | #[test] | |
166 | fn len_fn_different_return_type() { | |
167 | cov_mark::check!(len_fn_different_return_type); | |
168 | check_assist_not_applicable( | |
169 | generate_is_empty_from_len, | |
170 | r#" | |
171 | struct MyStruct { data: Vec<String> } | |
172 | ||
173 | impl MyStruct { | |
174 | #[must_use] | |
175 | p$0ub fn len(&self) -> u32 { | |
176 | self.data.len() | |
177 | } | |
178 | } | |
179 | "#, | |
180 | ); | |
181 | } | |
182 | ||
183 | #[test] | |
184 | fn generate_is_empty() { | |
185 | check_assist( | |
186 | generate_is_empty_from_len, | |
187 | r#" | |
188 | struct MyStruct { data: Vec<String> } | |
189 | ||
190 | impl MyStruct { | |
191 | #[must_use] | |
192 | p$0ub fn len(&self) -> usize { | |
193 | self.data.len() | |
194 | } | |
195 | } | |
196 | "#, | |
197 | r#" | |
198 | struct MyStruct { data: Vec<String> } | |
199 | ||
200 | impl MyStruct { | |
201 | #[must_use] | |
202 | pub fn len(&self) -> usize { | |
203 | self.data.len() | |
204 | } | |
205 | ||
206 | #[must_use] | |
207 | pub fn is_empty(&self) -> bool { | |
208 | self.len() == 0 | |
209 | } | |
210 | } | |
211 | "#, | |
212 | ); | |
213 | } | |
214 | ||
215 | #[test] | |
216 | fn multiple_functions_in_impl() { | |
217 | check_assist( | |
218 | generate_is_empty_from_len, | |
219 | r#" | |
220 | struct MyStruct { data: Vec<String> } | |
221 | ||
222 | impl MyStruct { | |
223 | #[must_use] | |
224 | pub fn new() -> Self { | |
225 | Self { data: 0 } | |
226 | } | |
227 | ||
228 | #[must_use] | |
229 | p$0ub fn len(&self) -> usize { | |
230 | self.data.len() | |
231 | } | |
232 | ||
233 | pub fn work(&self) -> Option<usize> { | |
234 | ||
235 | } | |
236 | } | |
237 | "#, | |
238 | r#" | |
239 | struct MyStruct { data: Vec<String> } | |
240 | ||
241 | impl MyStruct { | |
242 | #[must_use] | |
243 | pub fn new() -> Self { | |
244 | Self { data: 0 } | |
245 | } | |
246 | ||
247 | #[must_use] | |
248 | pub fn len(&self) -> usize { | |
249 | self.data.len() | |
250 | } | |
251 | ||
252 | #[must_use] | |
253 | pub fn is_empty(&self) -> bool { | |
254 | self.len() == 0 | |
255 | } | |
256 | ||
257 | pub fn work(&self) -> Option<usize> { | |
258 | ||
259 | } | |
260 | } | |
261 | "#, | |
262 | ); | |
263 | } | |
264 | ||
265 | #[test] | |
266 | fn multiple_impls() { | |
267 | check_assist_not_applicable( | |
268 | generate_is_empty_from_len, | |
269 | r#" | |
270 | struct MyStruct { data: Vec<String> } | |
271 | ||
272 | impl MyStruct { | |
273 | #[must_use] | |
274 | p$0ub fn len(&self) -> usize { | |
275 | self.data.len() | |
276 | } | |
277 | } | |
278 | ||
279 | impl MyStruct { | |
280 | #[must_use] | |
281 | pub fn is_empty(&self) -> bool { | |
282 | self.len() == 0 | |
283 | } | |
284 | } | |
285 | "#, | |
286 | ); | |
287 | } | |
288 | } |