]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_default_from_new.rs
Merge #7799
[rust.git] / crates / ide_assists / src / handlers / generate_default_from_new.rs
1 use crate::{
2     assist_context::{AssistContext, Assists},
3     AssistId,
4 };
5 use ide_db::helpers::FamousDefs;
6 use 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 // ```
40 pub(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
74 fn default_fn_node_for_new(impl_: Impl) -> String {
75     format!(
76         "
77
78 impl Default for {} {{
79     fn default() -> Self {{
80         Self::new()
81     }}
82 }}",
83         impl_.self_ty().unwrap().syntax().text()
84     )
85 }
86
87 fn 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)]
107 mod 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#"
118 struct Example { _inner: () }
119
120 impl Example {
121     pub fn ne$0w() -> Self {
122         Self { _inner: () }
123     }
124 }
125
126 fn main() {}
127 "#,
128             r#"
129 struct Example { _inner: () }
130
131 impl Example {
132     pub fn new() -> Self {
133         Self { _inner: () }
134     }
135 }
136
137 impl Default for Example {
138     fn default() -> Self {
139         Self::new()
140     }
141 }
142
143 fn main() {}
144 "#,
145         );
146     }
147
148     #[test]
149     fn generate_default2() {
150         check_pass(
151             r#"
152 struct Test { value: u32 }
153
154 impl Test {
155     pub fn ne$0w() -> Self {
156         Self { value: 0 }
157     }
158 }
159 "#,
160             r#"
161 struct Test { value: u32 }
162
163 impl Test {
164     pub fn new() -> Self {
165         Self { value: 0 }
166     }
167 }
168
169 impl 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#"
183 struct Example { _inner: () }
184
185 impl 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#"
199 struct Example { _inner: () }
200
201 impl 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#"
216 struct Example { _inner: () }
217
218 impl Example {
219     pub fn n$0ew() -> Self {
220         Self { _inner: () }
221     }
222 }
223
224 impl 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#"
237 fn n$0ew() -> u32 {
238     0
239 }
240 "#,
241         );
242     }
243
244     #[test]
245     fn multiple_struct_blocks() {
246         check_pass(
247             r#"
248 struct Example { _inner: () }
249 struct Test { value: u32 }
250
251 impl Example {
252     pub fn new$0() -> Self {
253         Self { _inner: () }
254     }
255 }
256 "#,
257             r#"
258 struct Example { _inner: () }
259 struct Test { value: u32 }
260
261 impl Example {
262     pub fn new() -> Self {
263         Self { _inner: () }
264     }
265 }
266
267 impl 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#"
280 impl Example {
281     pub fn $0new() -> Self {
282         Self { _inner: () }
283     }
284 }
285
286 struct Example { _inner: () }
287 "#,
288             r#"
289 impl Example {
290     pub fn new() -> Self {
291         Self { _inner: () }
292     }
293 }
294
295 impl Default for Example {
296     fn default() -> Self {
297         Self::new()
298     }
299 }
300
301 struct Example { _inner: () }
302 "#,
303         );
304     }
305
306     #[test]
307     fn struct_in_module() {
308         check_pass(
309             r#"
310 mod test {
311     struct Example { _inner: () }
312
313     impl Example {
314         pub fn n$0ew() -> Self {
315             Self { _inner: () }
316         }
317     }
318 }
319 "#,
320             r#"
321 mod test {
322     struct Example { _inner: () }
323
324     impl Example {
325         pub fn new() -> Self {
326             Self { _inner: () }
327         }
328     }
329
330 impl 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#"
345 mod 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 }