]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_default_from_new.rs
7708: rust ideomatic code fixes.
[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         mark::hit!(default_block_is_already_present);
58         mark::hit!(struct_in_module_with_default);
59         return None;
60     }
61
62     let insert_location = impl_.syntax().text_range();
63
64     acc.add(
65         AssistId("generate_default_from_new", crate::AssistKind::Generate),
66         "Generate a Default impl from a new fn",
67         insert_location,
68         move |builder| {
69             let code = default_fn_node_for_new(impl_);
70             builder.insert(insert_location.end(), code);
71         },
72     )
73 }
74
75 fn default_fn_node_for_new(impl_: Impl) -> String {
76     format!(
77         "
78
79 impl Default for {} {{
80     fn default() -> Self {{
81         Self::new()
82     }}
83 }}",
84         impl_.self_ty().unwrap().syntax().text()
85     )
86 }
87
88 fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool {
89     let db = ctx.sema.db;
90     let impl_ = ctx.sema.to_def(impl_);
91     let impl_def = match impl_ {
92         Some(value) => value,
93         None => return false,
94     };
95
96     let ty = impl_def.target_ty(db);
97     let krate = impl_def.module(db).krate();
98     let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default();
99     let default_trait = match default {
100         Some(value) => 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 Example {
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         mark::check!(default_block_is_already_present);
215         check_not_applicable(
216             r#"
217 struct Example { _inner: () }
218
219 impl Example {
220     pub fn n$0ew() -> Self {
221         Self { _inner: () }
222     }
223 }
224
225 impl Default for Example {
226     fn default() -> Self {
227         Self::new()
228     }
229 }
230 "#,
231         );
232     }
233
234     #[test]
235     fn standalone_new_function() {
236         check_not_applicable(
237             r#"
238 fn n$0ew() -> u32 {
239     0
240 }
241 "#,
242         );
243     }
244
245     #[test]
246     fn multiple_struct_blocks() {
247         check_pass(
248             r#"
249 struct Example { _inner: () }
250 struct Test { value: u32 }
251
252 impl Example {
253     pub fn new$0() -> Self {
254         Self { _inner: () }
255     }
256 }
257 "#,
258             r#"
259 struct Example { _inner: () }
260 struct Test { value: u32 }
261
262 impl Example {
263     pub fn new() -> Self {
264         Self { _inner: () }
265     }
266 }
267
268 impl Default for Example {
269     fn default() -> Self {
270         Self::new()
271     }
272 }
273 "#,
274         );
275     }
276
277     #[test]
278     fn when_struct_is_after_impl() {
279         check_pass(
280             r#"
281 impl Example {
282     pub fn $0new() -> Self {
283         Self { _inner: () }
284     }
285 }
286
287 struct Example { _inner: () }
288 "#,
289             r#"
290 impl Example {
291     pub fn new() -> Self {
292         Self { _inner: () }
293     }
294 }
295
296 impl Default for Example {
297     fn default() -> Self {
298         Self::new()
299     }
300 }
301
302 struct Example { _inner: () }
303 "#,
304         );
305     }
306
307     #[test]
308     fn struct_in_module() {
309         check_pass(
310             r#"
311 mod test {
312     struct Example { _inner: () }
313
314     impl Example {
315         pub fn n$0ew() -> Self {
316             Self { _inner: () }
317         }
318     }
319 }
320 "#,
321             r#"
322 mod test {
323     struct Example { _inner: () }
324
325     impl Example {
326         pub fn new() -> Self {
327             Self { _inner: () }
328         }
329     }
330
331 impl Default for Example {
332     fn default() -> Self {
333         Self::new()
334     }
335 }
336 }
337 "#,
338         );
339     }
340
341     #[test]
342     fn struct_in_module_with_default() {
343         mark::check!(struct_in_module_with_default);
344         check_not_applicable(
345             r#"
346 mod test {
347     struct Example { _inner: () }
348
349     impl Example {
350         pub fn n$0ew() -> Self {
351             Self { _inner: () }
352         }
353     }
354
355     impl Default for Example {
356         fn default() -> Self {
357             Self::new()
358         }
359     }
360 }
361 "#,
362         );
363     }
364
365     fn check_pass(before: &str, after: &str) {
366         let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
367         check_assist(generate_default_from_new, before, after);
368     }
369
370     fn check_not_applicable(before: &str) {
371         let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
372         check_assist_not_applicable(generate_default_from_new, before);
373     }
374 }