aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/generate_default_from_new.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers/generate_default_from_new.rs')
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_new.rs373
1 files changed, 373 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs
new file mode 100644
index 000000000..81c54ba3e
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs
@@ -0,0 +1,373 @@
1use crate::{
2 assist_context::{AssistContext, Assists},
3 AssistId,
4};
5use ide_db::helpers::FamousDefs;
6use syntax::{
7 ast::{self, Impl, NameOwner},
8 AstNode,
9};
10
11// Assist: generate_default_from_new
12//
13// Generates default implementation from new method.
14//
15// ```
16// struct Example { _inner: () }
17//
18// impl Example {
19// pub fn n$0ew() -> Self {
20// Self { _inner: () }
21// }
22// }
23// ```
24// ->
25// ```
26// struct Example { _inner: () }
27//
28// impl Example {
29// pub fn new() -> Self {
30// Self { _inner: () }
31// }
32// }
33//
34// impl Default for Example {
35// fn default() -> Self {
36// Self::new()
37// }
38// }
39// ```
40pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41 let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
42 let fn_name = fn_node.name()?;
43
44 if fn_name.text() != "new" {
45 cov_mark::hit!(other_function_than_new);
46 return None;
47 }
48
49 if fn_node.param_list()?.params().next().is_some() {
50 cov_mark::hit!(new_function_with_parameters);
51 return None;
52 }
53
54 let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?;
55 if is_default_implemented(ctx, &impl_) {
56 cov_mark::hit!(default_block_is_already_present);
57 cov_mark::hit!(struct_in_module_with_default);
58 return None;
59 }
60
61 let insert_location = impl_.syntax().text_range();
62
63 acc.add(
64 AssistId("generate_default_from_new", crate::AssistKind::Generate),
65 "Generate a Default impl from a new fn",
66 insert_location,
67 move |builder| {
68 let code = default_fn_node_for_new(impl_);
69 builder.insert(insert_location.end(), code);
70 },
71 )
72}
73
74fn default_fn_node_for_new(impl_: Impl) -> String {
75 format!(
76 "
77
78impl Default for {} {{
79 fn default() -> Self {{
80 Self::new()
81 }}
82}}",
83 impl_.self_ty().unwrap().syntax().text()
84 )
85}
86
87fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool {
88 let db = ctx.sema.db;
89 let impl_ = ctx.sema.to_def(impl_);
90 let impl_def = match impl_ {
91 Some(value) => value,
92 None => return false,
93 };
94
95 let ty = impl_def.target_ty(db);
96 let krate = impl_def.module(db).krate();
97 let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default();
98 let default_trait = match default {
99 Some(value) => value,
100 None => return false,
101 };
102
103 ty.impls_trait(db, default_trait, &[])
104}
105
106#[cfg(test)]
107mod tests {
108 use ide_db::helpers::FamousDefs;
109
110 use crate::tests::{check_assist, check_assist_not_applicable};
111
112 use super::*;
113
114 #[test]
115 fn generate_default() {
116 check_pass(
117 r#"
118struct Example { _inner: () }
119
120impl Example {
121 pub fn ne$0w() -> Self {
122 Self { _inner: () }
123 }
124}
125
126fn main() {}
127"#,
128 r#"
129struct Example { _inner: () }
130
131impl Example {
132 pub fn new() -> Self {
133 Self { _inner: () }
134 }
135}
136
137impl Default for Example {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143fn main() {}
144"#,
145 );
146 }
147
148 #[test]
149 fn generate_default2() {
150 check_pass(
151 r#"
152struct Test { value: u32 }
153
154impl Test {
155 pub fn ne$0w() -> Self {
156 Self { value: 0 }
157 }
158}
159"#,
160 r#"
161struct Test { value: u32 }
162
163impl Test {
164 pub fn new() -> Self {
165 Self { value: 0 }
166 }
167}
168
169impl Default for Test {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174"#,
175 );
176 }
177
178 #[test]
179 fn new_function_with_parameters() {
180 cov_mark::check!(new_function_with_parameters);
181 check_not_applicable(
182 r#"
183struct Example { _inner: () }
184
185impl Example {
186 pub fn $0new(value: ()) -> Self {
187 Self { _inner: value }
188 }
189}
190"#,
191 );
192 }
193
194 #[test]
195 fn other_function_than_new() {
196 cov_mark::check!(other_function_than_new);
197 check_not_applicable(
198 r#"
199struct Example { _inner: () }
200
201impl Example {
202 pub fn a$0dd() -> Self {
203 Self { _inner: () }
204 }
205}
206
207"#,
208 );
209 }
210
211 #[test]
212 fn default_block_is_already_present() {
213 cov_mark::check!(default_block_is_already_present);
214 check_not_applicable(
215 r#"
216struct Example { _inner: () }
217
218impl Example {
219 pub fn n$0ew() -> Self {
220 Self { _inner: () }
221 }
222}
223
224impl Default for Example {
225 fn default() -> Self {
226 Self::new()
227 }
228}
229"#,
230 );
231 }
232
233 #[test]
234 fn standalone_new_function() {
235 check_not_applicable(
236 r#"
237fn n$0ew() -> u32 {
238 0
239}
240"#,
241 );
242 }
243
244 #[test]
245 fn multiple_struct_blocks() {
246 check_pass(
247 r#"
248struct Example { _inner: () }
249struct Test { value: u32 }
250
251impl Example {
252 pub fn new$0() -> Self {
253 Self { _inner: () }
254 }
255}
256"#,
257 r#"
258struct Example { _inner: () }
259struct Test { value: u32 }
260
261impl Example {
262 pub fn new() -> Self {
263 Self { _inner: () }
264 }
265}
266
267impl Default for Example {
268 fn default() -> Self {
269 Self::new()
270 }
271}
272"#,
273 );
274 }
275
276 #[test]
277 fn when_struct_is_after_impl() {
278 check_pass(
279 r#"
280impl Example {
281 pub fn $0new() -> Self {
282 Self { _inner: () }
283 }
284}
285
286struct Example { _inner: () }
287"#,
288 r#"
289impl Example {
290 pub fn new() -> Self {
291 Self { _inner: () }
292 }
293}
294
295impl Default for Example {
296 fn default() -> Self {
297 Self::new()
298 }
299}
300
301struct Example { _inner: () }
302"#,
303 );
304 }
305
306 #[test]
307 fn struct_in_module() {
308 check_pass(
309 r#"
310mod test {
311 struct Example { _inner: () }
312
313 impl Example {
314 pub fn n$0ew() -> Self {
315 Self { _inner: () }
316 }
317 }
318}
319"#,
320 r#"
321mod test {
322 struct Example { _inner: () }
323
324 impl Example {
325 pub fn new() -> Self {
326 Self { _inner: () }
327 }
328 }
329
330impl Default for Example {
331 fn default() -> Self {
332 Self::new()
333 }
334}
335}
336"#,
337 );
338 }
339
340 #[test]
341 fn struct_in_module_with_default() {
342 cov_mark::check!(struct_in_module_with_default);
343 check_not_applicable(
344 r#"
345mod test {
346 struct Example { _inner: () }
347
348 impl Example {
349 pub fn n$0ew() -> Self {
350 Self { _inner: () }
351 }
352 }
353
354 impl Default for Example {
355 fn default() -> Self {
356 Self::new()
357 }
358 }
359}
360"#,
361 );
362 }
363
364 fn check_pass(before: &str, after: &str) {
365 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
366 check_assist(generate_default_from_new, before, after);
367 }
368
369 fn check_not_applicable(before: &str) {
370 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
371 check_assist_not_applicable(generate_default_from_new, before);
372 }
373}