Skip to main content

ide_completion/completions/
record.rs

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