]> git.lizzy.rs Git - rust.git/commitdiff
add suggestion ..Default::default() for remaining struct fields in a constructor...
authorBenjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Fri, 13 Nov 2020 16:17:16 +0000 (17:17 +0100)
committerBenjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Fri, 13 Nov 2020 16:17:16 +0000 (17:17 +0100)
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Cargo.lock
crates/assists/src/utils.rs
crates/completion/Cargo.toml
crates/completion/src/completions/record.rs

index 4940110684d021f7a8d00b34345aae961ac307d0..715a809789c70f527213d227d0cc5117572ede52 100644 (file)
@@ -253,6 +253,7 @@ dependencies = [
 name = "completion"
 version = "0.0.0"
 dependencies = [
+ "assists",
  "base_db",
  "expect-test",
  "hir",
index 7071fe96b16cbb64569762bedfba3551ee63950d..3fef75d57a9d75096c0eae5cf0312921348fc756 100644 (file)
@@ -257,6 +257,12 @@ pub trait From<T> {
     }
 }
 
+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 enum Option<T> { None, Some(T)}
 }
 
 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(crate) fn core_option_Option(&self) -> Option<Enum> {
         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")
     }
index b79ee33f73aeb855598ac454be916be8ba874032..3015ec9e0ea4e49bf149c555cd55989fdb77a911 100644 (file)
@@ -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" }
index 0f611084b03ffbb046525ccd63bb38b06587803e..2049b9d0911660e2d253ce44bc887e262e24c1c2 100644 (file)
@@ -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 @@ fn check(ra_fixture: &str, expect: Expect) {
         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(