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