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