]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_getter.rs
Merge #10592
[rust.git] / crates / ide_assists / src / handlers / generate_getter.rs
1 use ide_db::helpers::FamousDefs;
2 use stdx::{format_to, to_lower_snake_case};
3 use syntax::ast::{self, AstNode, HasName, HasVisibility};
4
5 use crate::{
6     utils::{convert_reference_type, find_impl_block_end, find_struct_impl, generate_impl_text},
7     AssistContext, AssistId, AssistKind, Assists, GroupLabel,
8 };
9
10 // Assist: generate_getter
11 //
12 // Generate a getter method.
13 //
14 // ```
15 // # //- minicore: as_ref
16 // # pub struct String;
17 // # impl AsRef<str> for String {
18 // #     fn as_ref(&self) -> &str {
19 // #         ""
20 // #     }
21 // # }
22 // #
23 // struct Person {
24 //     nam$0e: String,
25 // }
26 // ```
27 // ->
28 // ```
29 // # pub struct String;
30 // # impl AsRef<str> for String {
31 // #     fn as_ref(&self) -> &str {
32 // #         ""
33 // #     }
34 // # }
35 // #
36 // struct Person {
37 //     name: String,
38 // }
39 //
40 // impl Person {
41 //     /// Get a reference to the person's name.
42 //     fn $0name(&self) -> &str {
43 //         self.name.as_ref()
44 //     }
45 // }
46 // ```
47 pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
48     generate_getter_impl(acc, ctx, false)
49 }
50
51 // Assist: generate_getter_mut
52 //
53 // Generate a mut getter method.
54 //
55 // ```
56 // struct Person {
57 //     nam$0e: String,
58 // }
59 // ```
60 // ->
61 // ```
62 // struct Person {
63 //     name: String,
64 // }
65 //
66 // impl Person {
67 //     /// Get a mutable reference to the person's name.
68 //     fn $0name_mut(&mut self) -> &mut String {
69 //         &mut self.name
70 //     }
71 // }
72 // ```
73 pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
74     generate_getter_impl(acc, ctx, true)
75 }
76
77 pub(crate) fn generate_getter_impl(
78     acc: &mut Assists,
79     ctx: &AssistContext,
80     mutable: bool,
81 ) -> Option<()> {
82     let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
83     let field = ctx.find_node_at_offset::<ast::RecordField>()?;
84
85     let strukt_name = strukt.name()?;
86     let field_name = field.name()?;
87     let field_ty = field.ty()?;
88
89     // Return early if we've found an existing fn
90     let mut fn_name = to_lower_snake_case(&field_name.to_string());
91     if mutable {
92         format_to!(fn_name, "_mut");
93     }
94     let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
95
96     let (id, label) = if mutable {
97         ("generate_getter_mut", "Generate a mut getter method")
98     } else {
99         ("generate_getter", "Generate a getter method")
100     };
101     let target = field.syntax().text_range();
102     acc.add_group(
103         &GroupLabel("Generate getter/setter".to_owned()),
104         AssistId(id, AssistKind::Generate),
105         label,
106         target,
107         |builder| {
108             let mut buf = String::with_capacity(512);
109
110             if impl_def.is_some() {
111                 buf.push('\n');
112             }
113
114             let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
115             let (ty, body) = if mutable {
116                 (format!("&mut {}", field_ty), format!("&mut self.{}", field_name))
117             } else {
118                 let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(field_ty.syntax()).krate());
119                 ctx.sema
120                     .resolve_type(&field_ty)
121                     .and_then(|ty| convert_reference_type(ty, ctx.db(), famous_defs))
122                     .map(|conversion| {
123                         cov_mark::hit!(convert_reference_type);
124                         (
125                             conversion.convert_type(ctx.db()),
126                             conversion.getter(field_name.to_string()),
127                         )
128                     })
129                     .unwrap_or_else(|| (format!("&{}", field_ty), format!("&self.{}", field_name)))
130             };
131
132             format_to!(
133                 buf,
134                 "    /// Get a {}reference to the {}'s {}.
135     {}fn {}(&{}self) -> {} {{
136         {}
137     }}",
138                 mutable.then(|| "mutable ").unwrap_or_default(),
139                 to_lower_snake_case(&strukt_name.to_string()).replace('_', " "),
140                 fn_name.trim_end_matches("_mut").replace('_', " "),
141                 vis,
142                 fn_name,
143                 mutable.then(|| "mut ").unwrap_or_default(),
144                 ty,
145                 body,
146             );
147
148             let start_offset = impl_def
149                 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
150                 .unwrap_or_else(|| {
151                     buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
152                     strukt.syntax().text_range().end()
153                 });
154
155             match ctx.config.snippet_cap {
156                 Some(cap) => {
157                     builder.insert_snippet(cap, start_offset, buf.replacen("fn ", "fn $0", 1))
158                 }
159                 None => builder.insert(start_offset, buf),
160             }
161         },
162     )
163 }
164
165 #[cfg(test)]
166 mod tests {
167     use crate::tests::{check_assist, check_assist_not_applicable};
168
169     use super::*;
170
171     #[test]
172     fn test_generate_getter_from_field() {
173         check_assist(
174             generate_getter,
175             r#"
176 struct Context {
177     dat$0a: Data,
178 }
179 "#,
180             r#"
181 struct Context {
182     data: Data,
183 }
184
185 impl Context {
186     /// Get a reference to the context's data.
187     fn $0data(&self) -> &Data {
188         &self.data
189     }
190 }
191 "#,
192         );
193
194         check_assist(
195             generate_getter_mut,
196             r#"
197 struct Context {
198     dat$0a: Data,
199 }
200 "#,
201             r#"
202 struct Context {
203     data: Data,
204 }
205
206 impl Context {
207     /// Get a mutable reference to the context's data.
208     fn $0data_mut(&mut self) -> &mut Data {
209         &mut self.data
210     }
211 }
212 "#,
213         );
214     }
215
216     #[test]
217     fn test_generate_getter_already_implemented() {
218         check_assist_not_applicable(
219             generate_getter,
220             r#"
221 struct Context {
222     dat$0a: Data,
223 }
224
225 impl Context {
226     fn data(&self) -> &Data {
227         &self.data
228     }
229 }
230 "#,
231         );
232
233         check_assist_not_applicable(
234             generate_getter_mut,
235             r#"
236 struct Context {
237     dat$0a: Data,
238 }
239
240 impl Context {
241     fn data_mut(&mut self) -> &mut Data {
242         &mut self.data
243     }
244 }
245 "#,
246         );
247     }
248
249     #[test]
250     fn test_generate_getter_from_field_with_visibility_marker() {
251         check_assist(
252             generate_getter,
253             r#"
254 pub(crate) struct Context {
255     dat$0a: Data,
256 }
257 "#,
258             r#"
259 pub(crate) struct Context {
260     data: Data,
261 }
262
263 impl Context {
264     /// Get a reference to the context's data.
265     pub(crate) fn $0data(&self) -> &Data {
266         &self.data
267     }
268 }
269 "#,
270         );
271     }
272
273     #[test]
274     fn test_multiple_generate_getter() {
275         check_assist(
276             generate_getter,
277             r#"
278 struct Context {
279     data: Data,
280     cou$0nt: usize,
281 }
282
283 impl Context {
284     /// Get a reference to the context's data.
285     fn data(&self) -> &Data {
286         &self.data
287     }
288 }
289 "#,
290             r#"
291 struct Context {
292     data: Data,
293     count: usize,
294 }
295
296 impl Context {
297     /// Get a reference to the context's data.
298     fn data(&self) -> &Data {
299         &self.data
300     }
301
302     /// Get a reference to the context's count.
303     fn $0count(&self) -> &usize {
304         &self.count
305     }
306 }
307 "#,
308         );
309     }
310
311     #[test]
312     fn test_not_a_special_case() {
313         cov_mark::check_count!(convert_reference_type, 0);
314         // Fake string which doesn't implement AsRef<str>
315         check_assist(
316             generate_getter,
317             r#"
318 pub struct String;
319
320 struct S { foo: $0String }
321 "#,
322             r#"
323 pub struct String;
324
325 struct S { foo: String }
326
327 impl S {
328     /// Get a reference to the s's foo.
329     fn $0foo(&self) -> &String {
330         &self.foo
331     }
332 }
333 "#,
334         );
335     }
336
337     #[test]
338     fn test_convert_reference_type() {
339         cov_mark::check_count!(convert_reference_type, 6);
340
341         // Copy
342         check_assist(
343             generate_getter,
344             r#"
345 //- minicore: copy
346 struct S { foo: $0bool }
347 "#,
348             r#"
349 struct S { foo: bool }
350
351 impl S {
352     /// Get a reference to the s's foo.
353     fn $0foo(&self) -> bool {
354         self.foo
355     }
356 }
357 "#,
358         );
359
360         // AsRef<str>
361         check_assist(
362             generate_getter,
363             r#"
364 //- minicore: as_ref
365 pub struct String;
366 impl AsRef<str> for String {
367     fn as_ref(&self) -> &str {
368         ""
369     }
370 }
371
372 struct S { foo: $0String }
373 "#,
374             r#"
375 pub struct String;
376 impl AsRef<str> for String {
377     fn as_ref(&self) -> &str {
378         ""
379     }
380 }
381
382 struct S { foo: String }
383
384 impl S {
385     /// Get a reference to the s's foo.
386     fn $0foo(&self) -> &str {
387         self.foo.as_ref()
388     }
389 }
390 "#,
391         );
392
393         // AsRef<T>
394         check_assist(
395             generate_getter,
396             r#"
397 //- minicore: as_ref
398 struct Sweets;
399
400 pub struct Box<T>(T);
401 impl<T> AsRef<T> for Box<T> {
402     fn as_ref(&self) -> &T {
403         &self.0
404     }
405 }
406
407 struct S { foo: $0Box<Sweets> }
408 "#,
409             r#"
410 struct Sweets;
411
412 pub struct Box<T>(T);
413 impl<T> AsRef<T> for Box<T> {
414     fn as_ref(&self) -> &T {
415         &self.0
416     }
417 }
418
419 struct S { foo: Box<Sweets> }
420
421 impl S {
422     /// Get a reference to the s's foo.
423     fn $0foo(&self) -> &Sweets {
424         self.foo.as_ref()
425     }
426 }
427 "#,
428         );
429
430         // AsRef<[T]>
431         check_assist(
432             generate_getter,
433             r#"
434 //- minicore: as_ref
435 pub struct Vec<T>;
436 impl<T> AsRef<[T]> for Vec<T> {
437     fn as_ref(&self) -> &[T] {
438         &[]
439     }
440 }
441
442 struct S { foo: $0Vec<()> }
443 "#,
444             r#"
445 pub struct Vec<T>;
446 impl<T> AsRef<[T]> for Vec<T> {
447     fn as_ref(&self) -> &[T] {
448         &[]
449     }
450 }
451
452 struct S { foo: Vec<()> }
453
454 impl S {
455     /// Get a reference to the s's foo.
456     fn $0foo(&self) -> &[()] {
457         self.foo.as_ref()
458     }
459 }
460 "#,
461         );
462
463         // Option
464         check_assist(
465             generate_getter,
466             r#"
467 //- minicore: option
468 struct Failure;
469
470 struct S { foo: $0Option<Failure> }
471 "#,
472             r#"
473 struct Failure;
474
475 struct S { foo: Option<Failure> }
476
477 impl S {
478     /// Get a reference to the s's foo.
479     fn $0foo(&self) -> Option<&Failure> {
480         self.foo.as_ref()
481     }
482 }
483 "#,
484         );
485
486         // Result
487         check_assist(
488             generate_getter,
489             r#"
490 //- minicore: result
491 struct Context {
492     dat$0a: Result<bool, i32>,
493 }
494 "#,
495             r#"
496 struct Context {
497     data: Result<bool, i32>,
498 }
499
500 impl Context {
501     /// Get a reference to the context's data.
502     fn $0data(&self) -> Result<&bool, &i32> {
503         self.data.as_ref()
504     }
505 }
506 "#,
507         );
508     }
509 }