]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / generate_is_empty_from_len.rs
CommitLineData
064997fb
FG
1use hir::{known, HasSource, Name};
2use syntax::{
3 ast::{self, HasName},
4 AstNode,
5};
6
7use 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// ```
42pub(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
88fn 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)]
102mod 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#"
113struct MyStruct { data: Vec<String> }
114
115impl 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#"
130struct MyStruct { data: Vec<String> }
131
132impl 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#"
148struct MyStruct { data: Vec<String> }
149
150impl 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#"
171struct MyStruct { data: Vec<String> }
172
173impl 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#"
188struct MyStruct { data: Vec<String> }
189
190impl MyStruct {
191 #[must_use]
192 p$0ub fn len(&self) -> usize {
193 self.data.len()
194 }
195}
196"#,
197 r#"
198struct MyStruct { data: Vec<String> }
199
200impl 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#"
220struct MyStruct { data: Vec<String> }
221
222impl 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#"
239struct MyStruct { data: Vec<String> }
240
241impl 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#"
270struct MyStruct { data: Vec<String> }
271
272impl MyStruct {
273 #[must_use]
274 p$0ub fn len(&self) -> usize {
275 self.data.len()
276 }
277}
278
279impl MyStruct {
280 #[must_use]
281 pub fn is_empty(&self) -> bool {
282 self.len() == 0
283 }
284}
285"#,
286 );
287 }
288}