]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_default_from_new.rs
7708: handle both FamousDefs fixture and plain code.
[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 use test_utils::mark;
11
12 // Assist: generate_default_from_new
13 //
14 // Generates default implementation from new method.
15 //
16 // ```
17 // struct Example { _inner: () }
18 //
19 // impl Example {
20 //     pub fn n$0ew() -> Self {
21 //         Self { _inner: () }
22 //     }
23 // }
24 // ```
25 // ->
26 // ```
27 // struct Example { _inner: () }
28 //
29 // impl Example {
30 //     pub fn new() -> Self {
31 //         Self { _inner: () }
32 //     }
33 // }
34 //
35 // impl Default for Example {
36 //     fn default() -> Self {
37 //         Self::new()
38 //     }
39 // }
40 // ```
41 pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42     let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
43     let fn_name = fn_node.name()?;
44
45     if fn_name.text() != "new" {
46         mark::hit!(other_function_than_new);
47         return None;
48     }
49
50     if fn_node.param_list()?.params().next().is_some() {
51         mark::hit!(new_function_with_parameters);
52         return None;
53     }
54
55     let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?;
56     if is_default_implemented(ctx, &impl_) {
57         return None;
58     }
59
60     let insert_location = impl_.syntax().text_range();
61
62     acc.add(
63         AssistId("generate_default_from_new", crate::AssistKind::Generate),
64         "Generate a Default impl from a new fn",
65         insert_location,
66         move |builder| {
67             let code = default_fn_node_for_new(impl_);
68             builder.insert(insert_location.end(), code);
69         },
70     )
71 }
72
73 fn default_fn_node_for_new(impl_: Impl) -> String {
74     format!(
75         "
76
77 impl Default for {} {{
78     fn default() -> Self {{
79         Self::new()
80     }}
81 }}",
82         impl_.self_ty().unwrap().syntax().text()
83     )
84 }
85
86 fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool {
87     let db = ctx.sema.db;
88     let impl_ = ctx.sema.to_def(impl_);
89     let impl_def;
90     match impl_ {
91         Some(value) => impl_def = 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;
99     match default {
100         Some(value) => default_trait = value,
101         None => return false,
102     }
103
104     ty.impls_trait(db, default_trait, &[])
105 }
106
107 #[cfg(test)]
108 mod tests {
109     use ide_db::helpers::FamousDefs;
110
111     use crate::tests::{check_assist, check_assist_not_applicable};
112
113     use super::*;
114
115     #[test]
116     fn generate_default() {
117         check_pass(
118             r#"
119 struct Example { _inner: () }
120
121 impl Example {
122     pub fn ne$0w() -> Self {
123         Self { _inner: () }
124     }
125 }
126
127 fn main() {}
128 "#,
129             r#"
130 struct Example { _inner: () }
131
132 impl Example {
133     pub fn new() -> Self {
134         Self { _inner: () }
135     }
136 }
137
138 impl Default for Example {
139     fn default() -> Self {
140         Self::new()
141     }
142 }
143
144 fn main() {}
145 "#,
146         );
147     }
148
149     #[test]
150     fn generate_default2() {
151         check_pass(
152             r#"
153 struct Test { value: u32 }
154
155 impl Test {
156     pub fn ne$0w() -> Self {
157         Self { value: 0 }
158     }
159 }
160 "#,
161             r#"
162 struct Test { value: u32 }
163
164 impl Test {
165     pub fn new() -> Self {
166         Self { value: 0 }
167     }
168 }
169
170 impl Default for Test {
171     fn default() -> Self {
172         Self::new()
173     }
174 }
175 "#,
176         );
177     }
178
179     #[test]
180     fn new_function_with_parameters() {
181         mark::check!(new_function_with_parameters);
182         check_not_applicable(
183             r#"
184 struct Example { _inner: () }
185
186 impl Example {
187     pub fn $0new(value: ()) -> Self {
188         Self { _inner: value }
189     }
190 }
191 "#,
192         );
193     }
194
195     #[test]
196     fn other_function_than_new() {
197         mark::check!(other_function_than_new);
198         check_not_applicable(
199             r#"
200 struct Example { _inner: () }
201
202 impl Exmaple {
203     pub fn a$0dd() -> Self {
204         Self { _inner: () }
205     }
206 }
207
208 "#,
209         );
210     }
211
212     #[test]
213     fn 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         check_not_applicable(
343             r#"
344 mod test {
345     struct Example { _inner: () }
346
347     impl Example {
348         pub fn n$0ew() -> Self {
349             Self { _inner: () }
350         }
351     }
352
353     impl Default for Example {
354         fn default() -> Self {
355             Self::new()
356         }
357     }
358 }
359 "#,
360         );
361     }
362
363     fn check_pass(before: &str, after: &str) {
364         let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
365         check_assist(generate_default_from_new, before, after);
366     }
367
368     fn check_not_applicable(before: &str) {
369         let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
370         check_assist_not_applicable(generate_default_from_new, before);
371     }
372 }