From e73d140b51d7bd4b42cadf2dbd825b1dbc7cedb6 Mon Sep 17 00:00:00 2001
From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Date: Fri, 13 Nov 2020 17:17:16 +0100
Subject: [PATCH 1/2] add suggestion ..Default::default() for remaining struct
 fields in a constructor #6492

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
---
 Cargo.lock                                  |   1 +
 crates/assists/src/utils.rs                 |  12 ++-
 crates/completion/Cargo.toml                |   1 +
 crates/completion/src/completions/record.rs | 108 +++++++++++++++++++-
 4 files changed, 118 insertions(+), 4 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 4940110684d..715a809789c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -253,6 +253,7 @@ dependencies = [
 name = "completion"
 version = "0.0.0"
 dependencies = [
+ "assists",
  "base_db",
  "expect-test",
  "hir",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 7071fe96b16..3fef75d57a9 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -257,6 +257,12 @@ pub mod convert {
     }
 }
 
+pub mod default {
+    pub trait Default: Sized {
+       fn default() -> Self;
+    }
+}
+
 pub mod iter {
     pub use self::traits::{collect::IntoIterator, iterator::Iterator};
     mod traits {
@@ -327,7 +333,7 @@ pub mod option {
 }
 
 pub mod prelude {
-    pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}};
+    pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
 }
 #[prelude_import]
 pub use prelude::*;
@@ -345,6 +351,10 @@ pub use prelude::*;
         self.find_enum("core:option:Option")
     }
 
+    pub fn core_default_Default(&self) -> Option<Trait> {
+        self.find_trait("core:default:Default")
+    }
+
     pub fn core_iter_Iterator(&self) -> Option<Trait> {
         self.find_trait("core:iter:traits:iterator:Iterator")
     }
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml
index b79ee33f73a..3015ec9e0ea 100644
--- a/crates/completion/Cargo.toml
+++ b/crates/completion/Cargo.toml
@@ -14,6 +14,7 @@ itertools = "0.9.0"
 log = "0.4.8"
 rustc-hash = "1.1.0"
 
+assists = { path = "../assists", version = "0.0.0" }
 stdx = { path = "../stdx", version = "0.0.0" }
 syntax = { path = "../syntax", version = "0.0.0" }
 text_edit = { path = "../text_edit", version = "0.0.0" }
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs
index 0f611084b03..2049b9d0911 100644
--- a/crates/completion/src/completions/record.rs
+++ b/crates/completion/src/completions/record.rs
@@ -1,16 +1,43 @@
 //! Complete fields in record literals and patterns.
-use crate::{CompletionContext, Completions};
+use assists::utils::FamousDefs;
+use syntax::ast::Expr;
+
+use crate::{
+    item::CompletionKind, CompletionContext, CompletionItem, CompletionItemKind, Completions,
+};
 
 pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
     let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
         (None, None) => return None,
         (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
         (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
-        (_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit),
+        (_, Some(record_lit)) => {
+            let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone()));
+            let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
+            let impl_default_trait = default_trait
+                .and_then(|default_trait| ty.map(|ty| ty.impls_trait(ctx.db, default_trait, &[])))
+                .unwrap_or(false);
+
+            let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
+            if impl_default_trait && !missing_fields.is_empty() {
+                acc.add(
+                    CompletionItem::new(
+                        CompletionKind::Snippet,
+                        ctx.source_range(),
+                        "..Default::default()",
+                    )
+                    .insert_text("..Default::default()")
+                    .kind(CompletionItemKind::Field)
+                    .build(),
+                );
+            }
+
+            missing_fields
+        }
     };
 
     for (field, ty) in missing_fields {
-        acc.add_field(ctx, field, &ty)
+        acc.add_field(ctx, field, &ty);
     }
 
     Some(())
@@ -18,6 +45,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
 
 #[cfg(test)]
 mod tests {
+    use assists::utils::FamousDefs;
     use expect_test::{expect, Expect};
 
     use crate::{test_utils::completion_list, CompletionKind};
@@ -27,6 +55,80 @@ mod tests {
         expect.assert_eq(&actual);
     }
 
+    fn check_snippet(ra_fixture: &str, expect: Expect) {
+        let actual = completion_list(
+            &format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE),
+            CompletionKind::Snippet,
+        );
+        expect.assert_eq(&actual);
+    }
+
+    #[test]
+    fn test_record_literal_field_default() {
+        let test_code = r#"
+struct S { foo: u32, bar: usize }
+
+impl core::default::Default for S {
+    fn default() -> Self {
+        S {
+            foo: 0,
+            bar: 0,
+        }
+    }
+}
+
+fn process(f: S) {
+    let other = S {
+        foo: 5,
+        .<|>
+    };
+}
+"#;
+        check(
+            test_code,
+            expect![[r#"
+                fd bar usize
+            "#]],
+        );
+
+        check_snippet(
+            test_code,
+            expect![[r#"
+                fd ..Default::default()
+                sn pd
+                sn ppd
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_record_literal_field_without_default() {
+        let test_code = r#"
+struct S { foo: u32, bar: usize }
+
+fn process(f: S) {
+    let other = S {
+        foo: 5,
+        .<|>
+    };
+}
+"#;
+        check(
+            test_code,
+            expect![[r#"
+                fd bar usize
+            "#]],
+        );
+
+        check_snippet(
+            test_code,
+            expect![[r#"
+                sn pd
+                sn ppd
+            "#]],
+        );
+    }
+
     #[test]
     fn test_record_pattern_field() {
         check(

From 9f15de77f92a4f3bdd76039d5a851e608d244c0f Mon Sep 17 00:00:00 2001
From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Date: Fri, 13 Nov 2020 23:20:26 +0100
Subject: [PATCH 2/2] add suggestion ..Default::default() for remaining struct
 fields in a constructor #6492

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
---
 crates/assists/src/utils.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 3fef75d57a9..7bd338e99c7 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -258,7 +258,7 @@ pub mod convert {
 }
 
 pub mod default {
-    pub trait Default: Sized {
+    pub trait Default {
        fn default() -> Self;
     }
 }