]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
:arrow_up: rust-analyzer
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / generate_new.rs
1 use ide_db::{
2     imports::import_assets::item_for_path_search, use_trivial_contructor::use_trivial_constructor,
3 };
4 use itertools::Itertools;
5 use stdx::format_to;
6 use syntax::ast::{self, AstNode, HasName, HasVisibility, StructKind};
7
8 use crate::{
9     utils::{find_impl_block_start, find_struct_impl, generate_impl_text},
10     AssistContext, AssistId, AssistKind, Assists,
11 };
12
13 // Assist: generate_new
14 //
15 // Adds a `fn new` for a type.
16 //
17 // ```
18 // struct Ctx<T: Clone> {
19 //      data: T,$0
20 // }
21 // ```
22 // ->
23 // ```
24 // struct Ctx<T: Clone> {
25 //      data: T,
26 // }
27 //
28 // impl<T: Clone> Ctx<T> {
29 //     fn $0new(data: T) -> Self { Self { data } }
30 // }
31 // ```
32 pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
33     let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
34
35     // We want to only apply this to non-union structs with named fields
36     let field_list = match strukt.kind() {
37         StructKind::Record(named) => named,
38         _ => return None,
39     };
40
41     // Return early if we've found an existing new fn
42     let impl_def =
43         find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &[String::from("new")])?;
44
45     let current_module = ctx.sema.scope(strukt.syntax())?.module();
46
47     let target = strukt.syntax().text_range();
48     acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
49         let mut buf = String::with_capacity(512);
50
51         if impl_def.is_some() {
52             buf.push('\n');
53         }
54
55         let vis = strukt.visibility().map_or(String::new(), |v| format!("{v} "));
56
57         let trivial_constructors = field_list
58             .fields()
59             .map(|f| {
60                 let name = f.name()?;
61
62                 let ty = ctx.sema.resolve_type(&f.ty()?)?;
63
64                 let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
65
66                 let type_path = current_module.find_use_path(
67                     ctx.sema.db,
68                     item_for_path_search(ctx.sema.db, item_in_ns)?,
69                     ctx.config.prefer_no_std,
70                 )?;
71
72                 let expr = use_trivial_constructor(
73                     &ctx.sema.db,
74                     ide_db::helpers::mod_path_to_ast(&type_path),
75                     &ty,
76                 )?;
77
78                 Some(format!("{name}: {expr}"))
79             })
80             .collect::<Vec<_>>();
81
82         let params = field_list
83             .fields()
84             .enumerate()
85             .filter_map(|(i, f)| {
86                 if trivial_constructors[i].is_none() {
87                     let name = f.name()?;
88                     let ty = f.ty()?;
89
90                     Some(format!("{name}: {ty}"))
91                 } else {
92                     None
93                 }
94             })
95             .format(", ");
96
97         let fields = field_list
98             .fields()
99             .enumerate()
100             .filter_map(|(i, f)| {
101                 let contructor = trivial_constructors[i].clone();
102                 if contructor.is_some() {
103                     contructor
104                 } else {
105                     Some(f.name()?.to_string())
106                 }
107             })
108             .format(", ");
109
110         format_to!(buf, "    {vis}fn new({params}) -> Self {{ Self {{ {fields} }} }}");
111
112         let start_offset = impl_def
113             .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
114             .unwrap_or_else(|| {
115                 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
116                 strukt.syntax().text_range().end()
117             });
118
119         match ctx.config.snippet_cap {
120             None => builder.insert(start_offset, buf),
121             Some(cap) => {
122                 buf = buf.replace("fn new", "fn $0new");
123                 builder.insert_snippet(cap, start_offset, buf);
124             }
125         }
126     })
127 }
128
129 #[cfg(test)]
130 mod tests {
131     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
132
133     use super::*;
134
135     #[test]
136     fn test_generate_new_with_zst_fields() {
137         check_assist(
138             generate_new,
139             r#"
140 struct Empty;
141
142 struct Foo { empty: Empty $0}
143 "#,
144             r#"
145 struct Empty;
146
147 struct Foo { empty: Empty }
148
149 impl Foo {
150     fn $0new() -> Self { Self { empty: Empty } }
151 }
152 "#,
153         );
154         check_assist(
155             generate_new,
156             r#"
157 struct Empty;
158
159 struct Foo { baz: String, empty: Empty $0}
160 "#,
161             r#"
162 struct Empty;
163
164 struct Foo { baz: String, empty: Empty }
165
166 impl Foo {
167     fn $0new(baz: String) -> Self { Self { baz, empty: Empty } }
168 }
169 "#,
170         );
171         check_assist(
172             generate_new,
173             r#"
174 enum Empty { Bar }
175
176 struct Foo { empty: Empty $0}
177 "#,
178             r#"
179 enum Empty { Bar }
180
181 struct Foo { empty: Empty }
182
183 impl Foo {
184     fn $0new() -> Self { Self { empty: Empty::Bar } }
185 }
186 "#,
187         );
188
189         // make sure the assist only works on unit variants
190         check_assist(
191             generate_new,
192             r#"
193 struct Empty {}
194
195 struct Foo { empty: Empty $0}
196 "#,
197             r#"
198 struct Empty {}
199
200 struct Foo { empty: Empty }
201
202 impl Foo {
203     fn $0new(empty: Empty) -> Self { Self { empty } }
204 }
205 "#,
206         );
207         check_assist(
208             generate_new,
209             r#"
210 enum Empty { Bar {} }
211
212 struct Foo { empty: Empty $0}
213 "#,
214             r#"
215 enum Empty { Bar {} }
216
217 struct Foo { empty: Empty }
218
219 impl Foo {
220     fn $0new(empty: Empty) -> Self { Self { empty } }
221 }
222 "#,
223         );
224     }
225
226     #[test]
227     fn test_generate_new() {
228         check_assist(
229             generate_new,
230             r#"
231 struct Foo {$0}
232 "#,
233             r#"
234 struct Foo {}
235
236 impl Foo {
237     fn $0new() -> Self { Self {  } }
238 }
239 "#,
240         );
241         check_assist(
242             generate_new,
243             r#"
244 struct Foo<T: Clone> {$0}
245 "#,
246             r#"
247 struct Foo<T: Clone> {}
248
249 impl<T: Clone> Foo<T> {
250     fn $0new() -> Self { Self {  } }
251 }
252 "#,
253         );
254         check_assist(
255             generate_new,
256             r#"
257 struct Foo<'a, T: Foo<'a>> {$0}
258 "#,
259             r#"
260 struct Foo<'a, T: Foo<'a>> {}
261
262 impl<'a, T: Foo<'a>> Foo<'a, T> {
263     fn $0new() -> Self { Self {  } }
264 }
265 "#,
266         );
267         check_assist(
268             generate_new,
269             r#"
270 struct Foo { baz: String $0}
271 "#,
272             r#"
273 struct Foo { baz: String }
274
275 impl Foo {
276     fn $0new(baz: String) -> Self { Self { baz } }
277 }
278 "#,
279         );
280         check_assist(
281             generate_new,
282             r#"
283 struct Foo { baz: String, qux: Vec<i32> $0}
284 "#,
285             r#"
286 struct Foo { baz: String, qux: Vec<i32> }
287
288 impl Foo {
289     fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
290 }
291 "#,
292         );
293     }
294
295     #[test]
296     fn check_that_visibility_modifiers_dont_get_brought_in() {
297         check_assist(
298             generate_new,
299             r#"
300 struct Foo { pub baz: String, pub qux: Vec<i32> $0}
301 "#,
302             r#"
303 struct Foo { pub baz: String, pub qux: Vec<i32> }
304
305 impl Foo {
306     fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
307 }
308 "#,
309         );
310     }
311
312     #[test]
313     fn check_it_reuses_existing_impls() {
314         check_assist(
315             generate_new,
316             r#"
317 struct Foo {$0}
318
319 impl Foo {}
320 "#,
321             r#"
322 struct Foo {}
323
324 impl Foo {
325     fn $0new() -> Self { Self {  } }
326 }
327 "#,
328         );
329         check_assist(
330             generate_new,
331             r#"
332 struct Foo {$0}
333
334 impl Foo {
335     fn qux(&self) {}
336 }
337 "#,
338             r#"
339 struct Foo {}
340
341 impl Foo {
342     fn $0new() -> Self { Self {  } }
343
344     fn qux(&self) {}
345 }
346 "#,
347         );
348
349         check_assist(
350             generate_new,
351             r#"
352 struct Foo {$0}
353
354 impl Foo {
355     fn qux(&self) {}
356     fn baz() -> i32 {
357         5
358     }
359 }
360 "#,
361             r#"
362 struct Foo {}
363
364 impl Foo {
365     fn $0new() -> Self { Self {  } }
366
367     fn qux(&self) {}
368     fn baz() -> i32 {
369         5
370     }
371 }
372 "#,
373         );
374     }
375
376     #[test]
377     fn check_visibility_of_new_fn_based_on_struct() {
378         check_assist(
379             generate_new,
380             r#"
381 pub struct Foo {$0}
382 "#,
383             r#"
384 pub struct Foo {}
385
386 impl Foo {
387     pub fn $0new() -> Self { Self {  } }
388 }
389 "#,
390         );
391         check_assist(
392             generate_new,
393             r#"
394 pub(crate) struct Foo {$0}
395 "#,
396             r#"
397 pub(crate) struct Foo {}
398
399 impl Foo {
400     pub(crate) fn $0new() -> Self { Self {  } }
401 }
402 "#,
403         );
404     }
405
406     #[test]
407     fn generate_new_not_applicable_if_fn_exists() {
408         check_assist_not_applicable(
409             generate_new,
410             r#"
411 struct Foo {$0}
412
413 impl Foo {
414     fn new() -> Self {
415         Self
416     }
417 }
418 "#,
419         );
420
421         check_assist_not_applicable(
422             generate_new,
423             r#"
424 struct Foo {$0}
425
426 impl Foo {
427     fn New() -> Self {
428         Self
429     }
430 }
431 "#,
432         );
433     }
434
435     #[test]
436     fn generate_new_target() {
437         check_assist_target(
438             generate_new,
439             r#"
440 struct SomeThingIrrelevant;
441 /// Has a lifetime parameter
442 struct Foo<'a, T: Foo<'a>> {$0}
443 struct EvenMoreIrrelevant;
444 "#,
445             "/// Has a lifetime parameter
446 struct Foo<'a, T: Foo<'a>> {}",
447         );
448     }
449
450     #[test]
451     fn test_unrelated_new() {
452         check_assist(
453             generate_new,
454             r#"
455 pub struct AstId<N: AstNode> {
456     file_id: HirFileId,
457     file_ast_id: FileAstId<N>,
458 }
459
460 impl<N: AstNode> AstId<N> {
461     pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
462         AstId { file_id, file_ast_id }
463     }
464 }
465
466 pub struct Source<T> {
467     pub file_id: HirFileId,$0
468     pub ast: T,
469 }
470
471 impl<T> Source<T> {
472     pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
473         Source { file_id: self.file_id, ast: f(self.ast) }
474     }
475 }
476 "#,
477             r#"
478 pub struct AstId<N: AstNode> {
479     file_id: HirFileId,
480     file_ast_id: FileAstId<N>,
481 }
482
483 impl<N: AstNode> AstId<N> {
484     pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
485         AstId { file_id, file_ast_id }
486     }
487 }
488
489 pub struct Source<T> {
490     pub file_id: HirFileId,
491     pub ast: T,
492 }
493
494 impl<T> Source<T> {
495     pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
496
497     pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
498         Source { file_id: self.file_id, ast: f(self.ast) }
499     }
500 }
501 "#,
502         );
503     }
504 }