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