]> git.lizzy.rs Git - rust.git/blobdiff - src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
:arrow_up: rust-analyzer
[rust.git] / src / tools / rust-analyzer / crates / ide-completion / src / completions / record.rs
index 1c9042390d3b2fc0703ce730cbbe659ff7703e7f..5d96fbd30a81d1c4dd02f8a38e68b00602096c61 100644 (file)
@@ -3,7 +3,7 @@
 use syntax::ast::{self, Expr};
 
 use crate::{
-    context::{DotAccess, DotAccessKind, ExprCtx, PathCompletionCtx, PatternContext, Qualified},
+    context::{DotAccess, DotAccessKind, PatternContext},
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
     CompletionRelevancePostfixMatch, Completions,
 };
@@ -14,7 +14,24 @@ pub(crate) fn complete_record_pattern_fields(
     pattern_ctx: &PatternContext,
 ) {
     if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx {
-        complete_fields(acc, ctx, ctx.sema.record_pattern_missing_fields(record_pat));
+        let ty = ctx.sema.type_of_pat(&ast::Pat::RecordPat(record_pat.clone()));
+        let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) {
+            Some(hir::Adt::Union(un)) => {
+                // ctx.sema.record_pat_missing_fields will always return
+                // an empty Vec on a union literal. This is normally
+                // reasonable, but here we'd like to present the full list
+                // of fields if the literal is empty.
+                let were_fields_specified =
+                    record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some();
+
+                match were_fields_specified {
+                    false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
+                    true => return,
+                }
+            }
+            _ => ctx.sema.record_pattern_missing_fields(record_pat),
+        };
+        complete_fields(acc, ctx, missing_fields);
     }
 }
 
@@ -42,8 +59,13 @@ pub(crate) fn complete_record_expr_fields(
         }
         _ => {
             let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
-            add_default_update(acc, ctx, ty, &missing_fields);
+
+            if !missing_fields.is_empty() {
+                cov_mark::hit!(functional_update_field);
+                add_default_update(acc, ctx, ty);
+            }
             if dot_prefix {
+                cov_mark::hit!(functional_update_one_dot);
                 let mut item =
                     CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
                 item.insert_text(".");
@@ -56,41 +78,18 @@ pub(crate) fn complete_record_expr_fields(
     complete_fields(acc, ctx, missing_fields);
 }
 
-// FIXME: This should probably be part of complete_path_expr
-pub(crate) fn complete_record_expr_func_update(
-    acc: &mut Completions,
-    ctx: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
-    expr_ctx: &ExprCtx,
-) {
-    if !matches!(path_ctx.qualified, Qualified::No) {
-        return;
-    }
-    if let ExprCtx { is_func_update: Some(record_expr), .. } = expr_ctx {
-        let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
-
-        match ty.as_ref().and_then(|t| t.original.as_adt()) {
-            Some(hir::Adt::Union(_)) => (),
-            _ => {
-                let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
-                add_default_update(acc, ctx, ty, &missing_fields);
-            }
-        };
-    }
-}
-
-fn add_default_update(
+pub(crate) fn add_default_update(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
     ty: Option<hir::TypeInfo>,
-    missing_fields: &[(hir::Field, hir::Type)],
 ) {
     let default_trait = ctx.famous_defs().core_default_Default();
-    let impl_default_trait = default_trait
+    let impls_default_trait = default_trait
         .zip(ty.as_ref())
         .map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
-    if impl_default_trait && !missing_fields.is_empty() {
+    if impls_default_trait {
         // FIXME: This should make use of scope_def like completions so we get all the other goodies
+        // that is we should handle this like actually completing the default function
         let completion_text = "..Default::default()";
         let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
         let completion_text =
@@ -130,7 +129,7 @@ mod tests {
     #[test]
     fn literal_struct_completion_edit() {
         check_edit(
-            "FooDesc {…}",
+            "FooDesc{}",
             r#"
 struct FooDesc { pub bar: bool }
 
@@ -155,7 +154,7 @@ fn baz() {
     #[test]
     fn literal_struct_impl_self_completion() {
         check_edit(
-            "Self {…}",
+            "Self{}",
             r#"
 struct Foo {
     bar: u64,
@@ -181,7 +180,7 @@ fn new() -> Foo {
         );
 
         check_edit(
-            "Self()",
+            "Self()",
             r#"
 mod submod {
     pub struct Foo(pub u64);
@@ -210,7 +209,7 @@ fn new() -> submod::Foo {
     #[test]
     fn literal_struct_completion_from_sub_modules() {
         check_edit(
-            "submod::Struct {…}",
+            "submod::Struct{}",
             r#"
 mod submod {
     pub struct Struct {
@@ -239,7 +238,7 @@ fn f() -> submod::Struct {
     #[test]
     fn literal_struct_complexion_module() {
         check_edit(
-            "FooDesc {…}",
+            "FooDesc{}",
             r#"
 mod _69latrick {
     pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }