Skip to main content

ide_completion/completions/
record.rs

1//! Complete fields in record literals and patterns.
2use hir::{HasContainer, ItemContainer};
3use ide_db::SymbolKind;
4use syntax::{
5    SmolStr, T,
6    algo::next_non_trivia_token,
7    ast::{self, Expr},
8};
9
10use crate::{
11    CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
12    CompletionRelevancePostfixMatch, Completions,
13    context::{DotAccess, DotAccessExprCtx, DotAccessKind, PatternContext},
14};
15
16pub(crate) fn complete_record_pattern_fields(
17    acc: &mut Completions,
18    ctx: &CompletionContext<'_, '_>,
19    pattern_ctx: &PatternContext,
20) {
21    if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx {
22        let ty = ctx.sema.type_of_pat(&ast::Pat::RecordPat(record_pat.clone()));
23        let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) {
24            Some(hir::Adt::Union(un)) => {
25                // ctx.sema.record_pat_missing_fields will always return
26                // an empty Vec on a union literal. This is normally
27                // reasonable, but here we'd like to present the full list
28                // of fields if the literal is empty.
29                let were_fields_specified =
30                    record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some();
31
32                match were_fields_specified {
33                    false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
34                    true => return,
35                }
36            }
37            _ => ctx.sema.record_pattern_matched_fields(record_pat),
38        };
39        complete_fields(acc, ctx, missing_fields);
40    }
41}
42
43pub(crate) fn complete_record_expr_fields(
44    acc: &mut Completions,
45    ctx: &CompletionContext<'_, '_>,
46    record_expr: &ast::RecordExpr,
47    &dot_prefix: &bool,
48) {
49    let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
50
51    let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) {
52        Some(hir::Adt::Union(un)) => {
53            // ctx.sema.record_literal_missing_fields will always return
54            // an empty Vec on a union literal. This is normally
55            // reasonable, but here we'd like to present the full list
56            // of fields if the literal is empty.
57            let were_fields_specified =
58                record_expr.record_expr_field_list().and_then(|fl| fl.fields().next()).is_some();
59
60            match were_fields_specified {
61                false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
62                true => return,
63            }
64        }
65        _ => {
66            let suggest_fields = ctx.sema.record_literal_matched_fields(record_expr);
67            let update_exists = record_expr
68                .record_expr_field_list()
69                .is_some_and(|list| list.dotdot_token().is_some());
70
71            if !suggest_fields.is_empty() && !update_exists {
72                cov_mark::hit!(functional_update_field);
73                add_default_update(acc, ctx, ty.as_ref());
74            }
75            if dot_prefix {
76                cov_mark::hit!(functional_update_one_dot);
77                let mut item = CompletionItem::new(
78                    CompletionItemKind::Snippet,
79                    ctx.source_range(),
80                    SmolStr::new_static(".."),
81                    ctx.edition,
82                );
83                item.insert_text(".");
84                item.add_to(acc, ctx.db);
85                return;
86            }
87            suggest_fields
88        }
89    };
90    complete_fields(acc, ctx, missing_fields);
91}
92
93pub(crate) fn add_default_update(
94    acc: &mut Completions,
95    ctx: &CompletionContext<'_, '_>,
96    ty: Option<&hir::TypeInfo<'_>>,
97) {
98    let default_trait = ctx.famous_defs().core_default_Default();
99    let impls_default_trait = default_trait
100        .zip(ty)
101        .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
102    let in_own_default_impl =
103        ty.zip(ctx.containing_function).is_some_and(|(ty, f)| ty.original == f.ret_type(ctx.db)
104            && matches!(f.container(ctx.db), ItemContainer::Impl(impl_) if impl_.trait_(ctx.db) == default_trait));
105    let ends_of_record_list =
106        next_non_trivia_token(ctx.token.clone()).is_none_or(|it| it.kind() == T!['}']);
107
108    if impls_default_trait && !in_own_default_impl && ends_of_record_list {
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}