ide_completion/completions/
record.rs

1//! Complete fields in record literals and patterns.
2use ide_db::SymbolKind;
3use syntax::{
4    SmolStr,
5    ast::{self, Expr},
6};
7
8use crate::{
9    CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
10    CompletionRelevancePostfixMatch, Completions,
11    context::{DotAccess, DotAccessExprCtx, DotAccessKind, PatternContext},
12};
13
14pub(crate) fn complete_record_pattern_fields(
15    acc: &mut Completions,
16    ctx: &CompletionContext<'_>,
17    pattern_ctx: &PatternContext,
18) {
19    if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx {
20        let ty = ctx.sema.type_of_pat(&ast::Pat::RecordPat(record_pat.clone()));
21        let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) {
22            Some(hir::Adt::Union(un)) => {
23                // ctx.sema.record_pat_missing_fields will always return
24                // an empty Vec on a union literal. This is normally
25                // reasonable, but here we'd like to present the full list
26                // of fields if the literal is empty.
27                let were_fields_specified =
28                    record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some();
29
30                match were_fields_specified {
31                    false => un
32                        .fields(ctx.db)
33                        .into_iter()
34                        .map(|f| (f, f.ty(ctx.db).to_type(ctx.db)))
35                        .collect(),
36                    true => return,
37                }
38            }
39            _ => ctx.sema.record_pattern_missing_fields(record_pat),
40        };
41        complete_fields(acc, ctx, missing_fields);
42    }
43}
44
45pub(crate) fn complete_record_expr_fields(
46    acc: &mut Completions,
47    ctx: &CompletionContext<'_>,
48    record_expr: &ast::RecordExpr,
49    &dot_prefix: &bool,
50) {
51    let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
52
53    let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) {
54        Some(hir::Adt::Union(un)) => {
55            // ctx.sema.record_literal_missing_fields will always return
56            // an empty Vec on a union literal. This is normally
57            // reasonable, but here we'd like to present the full list
58            // of fields if the literal is empty.
59            let were_fields_specified =
60                record_expr.record_expr_field_list().and_then(|fl| fl.fields().next()).is_some();
61
62            match were_fields_specified {
63                false => un
64                    .fields(ctx.db)
65                    .into_iter()
66                    .map(|f| (f, f.ty(ctx.db).to_type(ctx.db)))
67                    .collect(),
68                true => return,
69            }
70        }
71        _ => {
72            let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
73            let update_exists = record_expr
74                .record_expr_field_list()
75                .is_some_and(|list| list.dotdot_token().is_some());
76
77            if !missing_fields.is_empty() && !update_exists {
78                cov_mark::hit!(functional_update_field);
79                add_default_update(acc, ctx, ty);
80            }
81            if dot_prefix {
82                cov_mark::hit!(functional_update_one_dot);
83                let mut item = CompletionItem::new(
84                    CompletionItemKind::Snippet,
85                    ctx.source_range(),
86                    SmolStr::new_static(".."),
87                    ctx.edition,
88                );
89                item.insert_text(".");
90                item.add_to(acc, ctx.db);
91                return;
92            }
93            missing_fields
94        }
95    };
96    complete_fields(acc, ctx, missing_fields);
97}
98
99pub(crate) fn add_default_update(
100    acc: &mut Completions,
101    ctx: &CompletionContext<'_>,
102    ty: Option<hir::TypeInfo<'_>>,
103) {
104    let default_trait = ctx.famous_defs().core_default_Default();
105    let impls_default_trait = default_trait
106        .zip(ty.as_ref())
107        .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
108    if impls_default_trait {
109        // FIXME: This should make use of scope_def like completions so we get all the other goodies
110        // that is we should handle this like actually completing the default function
111        let completion_text = "..Default::default()";
112        let mut item = CompletionItem::new(
113            SymbolKind::Field,
114            ctx.source_range(),
115            SmolStr::new_static(completion_text),
116            ctx.edition,
117        );
118        let completion_text =
119            completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
120        item.insert_text(completion_text).set_relevance(CompletionRelevance {
121            postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
122            ..Default::default()
123        });
124        item.add_to(acc, ctx.db);
125    }
126}
127
128fn complete_fields(
129    acc: &mut Completions,
130    ctx: &CompletionContext<'_>,
131    missing_fields: Vec<(hir::Field, hir::Type<'_>)>,
132) {
133    for (field, ty) in missing_fields {
134        // This should call something else, we shouldn't be synthesizing a DotAccess here
135        acc.add_field(
136            ctx,
137            &DotAccess {
138                receiver: None,
139                receiver_ty: None,
140                kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal: false },
141                ctx: DotAccessExprCtx { in_block_expr: false, in_breakable: None },
142            },
143            None,
144            field,
145            &ty,
146        );
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use ide_db::SnippetCap;
153
154    use crate::{
155        CompletionConfig,
156        tests::{TEST_CONFIG, check_edit, check_edit_with_config},
157    };
158
159    #[test]
160    fn literal_struct_completion_edit() {
161        check_edit(
162            "FooDesc{}",
163            r#"
164struct FooDesc { pub bar: bool }
165
166fn create_foo(foo_desc: &FooDesc) -> () { () }
167
168fn baz() {
169    let foo = create_foo(&$0);
170}
171            "#,
172            r#"
173struct FooDesc { pub bar: bool }
174
175fn create_foo(foo_desc: &FooDesc) -> () { () }
176
177fn baz() {
178    let foo = create_foo(&FooDesc { bar: ${1:()} }$0);
179}
180            "#,
181        )
182    }
183
184    #[test]
185    fn literal_struct_completion_shorthand() {
186        check_edit(
187            "FooDesc{}",
188            r#"
189struct FooDesc { pub bar: bool, n: i32 }
190
191fn create_foo(foo_desc: &FooDesc) -> () { () }
192
193fn baz() {
194    let bar = true;
195    let foo = create_foo(&$0);
196}
197            "#,
198            r#"
199struct FooDesc { pub bar: bool, n: i32 }
200
201fn create_foo(foo_desc: &FooDesc) -> () { () }
202
203fn baz() {
204    let bar = true;
205    let foo = create_foo(&FooDesc { bar$1, n: ${2:()} }$0);
206}
207            "#,
208        )
209    }
210
211    #[test]
212    fn enum_variant_no_snippets() {
213        let conf = CompletionConfig { snippet_cap: SnippetCap::new(false), ..TEST_CONFIG };
214        // tuple variant
215        check_edit_with_config(
216            conf.clone(),
217            "Variant()",
218            r#"
219enum Enum {
220    Variant(usize),
221}
222
223impl Enum {
224    fn new(u: usize) -> Self {
225        Self::Va$0
226    }
227}
228"#,
229            r#"
230enum Enum {
231    Variant(usize),
232}
233
234impl Enum {
235    fn new(u: usize) -> Self {
236        Self::Variant
237    }
238}
239"#,
240        );
241
242        // record variant
243        check_edit_with_config(
244            conf,
245            "Variant{}",
246            r#"
247enum Enum {
248    Variant{u: usize},
249}
250
251impl Enum {
252    fn new(u: usize) -> Self {
253        Self::Va$0
254    }
255}
256"#,
257            r#"
258enum Enum {
259    Variant{u: usize},
260}
261
262impl Enum {
263    fn new(u: usize) -> Self {
264        Self::Variant
265    }
266}
267"#,
268        )
269    }
270
271    #[test]
272    fn literal_struct_impl_self_completion() {
273        check_edit(
274            "Self{}",
275            r#"
276struct Foo {
277    bar: u64,
278}
279
280impl Foo {
281    fn new() -> Foo {
282        Self$0
283    }
284}
285            "#,
286            r#"
287struct Foo {
288    bar: u64,
289}
290
291impl Foo {
292    fn new() -> Foo {
293        Self { bar: ${1:()} }$0
294    }
295}
296            "#,
297        );
298
299        check_edit(
300            "Self()",
301            r#"
302mod submod {
303    pub struct Foo(pub u64);
304}
305
306impl submod::Foo {
307    fn new() -> submod::Foo {
308        Self$0
309    }
310}
311            "#,
312            r#"
313mod submod {
314    pub struct Foo(pub u64);
315}
316
317impl submod::Foo {
318    fn new() -> submod::Foo {
319        Self(${1:()})$0
320    }
321}
322            "#,
323        )
324    }
325
326    #[test]
327    fn literal_struct_completion_from_sub_modules() {
328        check_edit(
329            "submod::Struct{}",
330            r#"
331mod submod {
332    pub struct Struct {
333        pub a: u64,
334    }
335}
336
337fn f() -> submod::Struct {
338    Stru$0
339}
340            "#,
341            r#"
342mod submod {
343    pub struct Struct {
344        pub a: u64,
345    }
346}
347
348fn f() -> submod::Struct {
349    submod::Struct { a: ${1:()} }$0
350}
351            "#,
352        )
353    }
354
355    #[test]
356    fn literal_struct_complexion_module() {
357        check_edit(
358            "FooDesc{}",
359            r#"
360mod _69latrick {
361    pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
362    pub fn create_foo(foo_desc: &FooDesc) -> () { () }
363}
364
365fn baz() {
366    use _69latrick::*;
367
368    let foo = create_foo(&$0);
369}
370            "#,
371            r#"
372mod _69latrick {
373    pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
374    pub fn create_foo(foo_desc: &FooDesc) -> () { () }
375}
376
377fn baz() {
378    use _69latrick::*;
379
380    let foo = create_foo(&FooDesc { six: ${1:()}, neuf: ${2:()}, bar: ${3:()} }$0);
381}
382            "#,
383        );
384    }
385
386    #[test]
387    fn default_completion_edit() {
388        check_edit(
389            "..Default::default()",
390            r#"
391//- minicore: default
392struct Struct { foo: u32, bar: usize }
393
394impl Default for Struct {
395    fn default() -> Self {}
396}
397
398fn foo() {
399    let other = Struct {
400        foo: 5,
401        .$0
402    };
403}
404"#,
405            r#"
406struct Struct { foo: u32, bar: usize }
407
408impl Default for Struct {
409    fn default() -> Self {}
410}
411
412fn foo() {
413    let other = Struct {
414        foo: 5,
415        ..Default::default()
416    };
417}
418"#,
419        );
420        check_edit(
421            "..Default::default()",
422            r#"
423//- minicore: default
424struct Struct { foo: u32, bar: usize }
425
426impl Default for Struct {
427    fn default() -> Self {}
428}
429
430fn foo() {
431    let other = Struct {
432        foo: 5,
433        $0
434    };
435}
436"#,
437            r#"
438struct Struct { foo: u32, bar: usize }
439
440impl Default for Struct {
441    fn default() -> Self {}
442}
443
444fn foo() {
445    let other = Struct {
446        foo: 5,
447        ..Default::default()
448    };
449}
450"#,
451        );
452        check_edit(
453            "..Default::default()",
454            r#"
455//- minicore: default
456struct Struct { foo: u32, bar: usize }
457
458impl Default for Struct {
459    fn default() -> Self {}
460}
461
462fn foo() {
463    let other = Struct {
464        foo: 5,
465        ..$0
466    };
467}
468"#,
469            r#"
470struct Struct { foo: u32, bar: usize }
471
472impl Default for Struct {
473    fn default() -> Self {}
474}
475
476fn foo() {
477    let other = Struct {
478        foo: 5,
479        ..Default::default()
480    };
481}
482"#,
483        );
484    }
485
486    #[test]
487    fn callable_field_struct_init() {
488        check_edit(
489            "field",
490            r#"
491struct S {
492    field: fn(),
493}
494
495fn main() {
496    S {fi$0
497}
498"#,
499            r#"
500struct S {
501    field: fn(),
502}
503
504fn main() {
505    S {field
506}
507"#,
508        );
509    }
510}