about summary refs log tree commit diff
diff options
context:
space:
mode:
authorShoyu Vanilla <modulo641@gmail.com>2025-01-23 00:27:31 +0900
committerShoyu Vanilla <modulo641@gmail.com>2025-01-27 19:38:35 +0900
commitd18afd394ce3342d04f020986bab0d82f51fdbdc (patch)
tree28f1d21accf9e137d072f2add4ac3a2c41cec5d7
parent1711a19dbff28d81e6c6df7503973a545ed74381 (diff)
downloadrust-d18afd394ce3342d04f020986bab0d82f51fdbdc.tar.gz
rust-d18afd394ce3342d04f020986bab0d82f51fdbdc.zip
feat: Implement `default-field-values`
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs27
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs59
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs86
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs34
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/from_id.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs64
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs15
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs18
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs12
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast59
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast70
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast28
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast47
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast39
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs6
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs4
33 files changed, 647 insertions, 31 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
index a55fec4f8b1..3177b2c74af 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
@@ -122,6 +122,9 @@ impl Body {
                     src.map(|it| it.expr())
                 }
                 DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
+                DefWithBodyId::FieldId(f) => {
+                    f.record_field_source(db).map(|it| it.and_then(|it| it.expr()))
+                }
             }
         };
         let module = def.module(db);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
index dfc716eb849..0d3a542a439 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
@@ -90,6 +90,7 @@ pub(super) fn lower_body(
         DefWithBodyId::ConstId(it) => db.attrs(it.into()),
         DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
         DefWithBodyId::VariantId(it) => db.attrs(it.into()),
+        DefWithBodyId::FieldId(it) => db.attrs(it.into()),
     }
     .rust_analyzer_tool()
     .any(|attr| *attr.path() == tool_path![skip]);
@@ -168,6 +169,7 @@ pub(super) fn lower_body(
                     Awaitable::No("constant")
                 }
                 DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
+                DefWithBodyId::FieldId(..) => Awaitable::No("field"),
             }
         },
     );
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
index 6a0b1e51979..6ba0bbd61c4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
@@ -11,6 +11,7 @@ use crate::{
         Statement,
     },
     pretty::{print_generic_args, print_path, print_type_ref},
+    VariantId,
 };
 
 use super::*;
@@ -56,6 +57,32 @@ pub(super) fn print_body_hir(
                 loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition),
             )
         }
+        DefWithBodyId::FieldId(it) => {
+            let parent_name: String = match it.parent {
+                VariantId::EnumVariantId(it) => {
+                    let loc = it.lookup(db);
+                    let enum_loc = loc.parent.lookup(db);
+                    format!(
+                        "{}::{}",
+                        enum_loc.id.item_tree(db)[enum_loc.id.value]
+                            .name
+                            .display(db.upcast(), edition),
+                        loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition),
+                    )
+                }
+                VariantId::StructId(it) => it
+                    .lookup(db)
+                    .id
+                    .resolved(db, |it| it.name.display(db.upcast(), edition).to_string()),
+                VariantId::UnionId(it) => it
+                    .lookup(db)
+                    .id
+                    .resolved(db, |it| it.name.display(db.upcast(), edition).to_string()),
+            };
+            let variant_data = it.parent.variant_data(db);
+            let field_name = &variant_data.fields()[it.local_id].name;
+            format!("field {}.{}", parent_name, field_name.display(db.upcast(), edition),)
+        }
     };
 
     let mut p = Printer {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index c8efd904320..95700b54db2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -55,6 +55,7 @@ pub mod visibility;
 
 use intern::Interned;
 pub use rustc_abi as layout;
+use src::HasSource;
 use triomphe::Arc;
 
 #[cfg(test)]
@@ -77,6 +78,7 @@ use hir_expand::{
     builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
     db::ExpandDatabase,
     eager::expand_eager_macro_input,
+    files::InFileWrapper,
     impl_intern_lookup,
     name::Name,
     proc_macro::{CustomProcMacroExpander, ProcMacroKind},
@@ -519,6 +521,41 @@ pub struct FieldId {
     pub local_id: LocalFieldId,
 }
 
+impl FieldId {
+    pub fn record_field_source(
+        &self,
+        db: &dyn DefDatabase,
+    ) -> InFileWrapper<HirFileId, Option<ast::RecordField>> {
+        let field_list = match self.parent {
+            crate::VariantId::EnumVariantId(it) => {
+                let s = it.lookup(db);
+                s.source(db).map(|it| {
+                    it.field_list().and_then(|it| match it {
+                        ast::FieldList::RecordFieldList(it) => Some(it),
+                        _ => None,
+                    })
+                })
+            }
+            crate::VariantId::StructId(it) => {
+                let s = it.lookup(db);
+                s.source(db).map(|it| {
+                    it.field_list().and_then(|it| match it {
+                        ast::FieldList::RecordFieldList(it) => Some(it),
+                        _ => None,
+                    })
+                })
+            }
+            crate::VariantId::UnionId(it) => {
+                let s = it.lookup(db);
+                s.source(db).map(|it| it.record_field_list())
+            }
+        };
+        field_list.map(|it| {
+            it.and_then(|it| it.fields().nth(self.local_id.into_raw().into_u32() as usize))
+        })
+    }
+}
+
 pub type LocalFieldId = Idx<data::adt::FieldData>;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -686,6 +723,7 @@ pub enum TypeOwnerId {
     TypeAliasId(TypeAliasId),
     ImplId(ImplId),
     EnumVariantId(EnumVariantId),
+    FieldId(FieldId),
 }
 
 impl TypeOwnerId {
@@ -703,6 +741,11 @@ impl TypeOwnerId {
                 GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent))
             }
             TypeOwnerId::InTypeConstId(_) => return None,
+            TypeOwnerId::FieldId(it) => GenericDefId::AdtId(match it.parent {
+                VariantId::EnumVariantId(it) => AdtId::EnumId(it.lookup(db).parent),
+                VariantId::StructId(it) => it.into(),
+                VariantId::UnionId(it) => it.into(),
+            }),
         })
     }
 }
@@ -717,7 +760,8 @@ impl_from!(
     TraitAliasId,
     TypeAliasId,
     ImplId,
-    EnumVariantId
+    EnumVariantId,
+    FieldId
     for TypeOwnerId
 );
 
@@ -730,6 +774,7 @@ impl From<DefWithBodyId> for TypeOwnerId {
             DefWithBodyId::ConstId(it) => it.into(),
             DefWithBodyId::InTypeConstId(it) => it.into(),
             DefWithBodyId::VariantId(it) => it.into(),
+            DefWithBodyId::FieldId(it) => it.into(),
         }
     }
 }
@@ -885,6 +930,7 @@ pub enum DefWithBodyId {
     ConstId(ConstId),
     InTypeConstId(InTypeConstId),
     VariantId(EnumVariantId),
+    FieldId(FieldId),
 }
 
 impl_from!(FunctionId, ConstId, StaticId, InTypeConstId for DefWithBodyId);
@@ -905,6 +951,7 @@ impl DefWithBodyId {
             // FIXME: stable rust doesn't allow generics in constants, but we should
             // use `TypeOwnerId::as_generic_def_id` when it does.
             DefWithBodyId::InTypeConstId(_) => None,
+            DefWithBodyId::FieldId(_) => None,
         }
     }
 }
@@ -1332,6 +1379,11 @@ impl HasModule for TypeOwnerId {
             TypeOwnerId::ImplId(it) => it.module(db),
             TypeOwnerId::EnumVariantId(it) => it.module(db),
             TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db),
+            TypeOwnerId::FieldId(it) => match it.parent {
+                VariantId::EnumVariantId(it) => it.module(db),
+                VariantId::StructId(it) => it.module(db),
+                VariantId::UnionId(it) => it.module(db),
+            },
         }
     }
 }
@@ -1344,6 +1396,11 @@ impl HasModule for DefWithBodyId {
             DefWithBodyId::ConstId(it) => it.module(db),
             DefWithBodyId::VariantId(it) => it.module(db),
             DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db),
+            DefWithBodyId::FieldId(it) => match it.parent {
+                VariantId::EnumVariantId(it) => it.module(db),
+                VariantId::StructId(it) => it.module(db),
+                VariantId::UnionId(it) => it.module(db),
+            },
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index c31d3221328..25391c910ea 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -211,6 +211,20 @@ enum Bar {
     #[default]
     Bar,
 }
+#[derive(Default)]
+struct Baz {
+    field1: i32 = 2,
+    field2: bool = { false },
+}
+#[derive(Default)]
+enum Qux {
+    #[default]
+    Foo {
+        field1: i32,
+        field2: bool = true,
+        field3: (),
+    }
+}
 "#,
         expect![[r#"
 #[derive(Default)]
@@ -224,6 +238,20 @@ enum Bar {
     #[default]
     Bar,
 }
+#[derive(Default)]
+struct Baz {
+    field1: i32 = 2,
+    field2: bool = { false },
+}
+#[derive(Default)]
+enum Qux {
+    #[default]
+    Foo {
+        field1: i32,
+        field2: bool = true,
+        field3: (),
+    }
+}
 
 impl <> $crate::default::Default for Foo< > where {
     fn default() -> Self {
@@ -236,6 +264,20 @@ impl <> $crate::default::Default for Bar< > where {
     fn default() -> Self {
         Bar::Bar
     }
+}
+impl <> $crate::default::Default for Baz< > where {
+    fn default() -> Self {
+        Baz {
+            ..
+        }
+    }
+}
+impl <> $crate::default::Default for Qux< > where {
+    fn default() -> Self {
+        Qux::Foo {
+            field1: $crate::default::Default::default(), field3: $crate::default::Default::default(), ..
+        }
+    }
 }"#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 7e13ae2f7a1..52998942965 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -1227,6 +1227,11 @@ impl HasResolver for TypeOwnerId {
             TypeOwnerId::TypeAliasId(it) => it.resolver(db),
             TypeOwnerId::ImplId(it) => it.resolver(db),
             TypeOwnerId::EnumVariantId(it) => it.resolver(db),
+            TypeOwnerId::FieldId(it) => match it.parent {
+                VariantId::EnumVariantId(it) => it.resolver(db),
+                VariantId::StructId(it) => it.resolver(db),
+                VariantId::UnionId(it) => it.resolver(db),
+            },
         }
     }
 }
@@ -1239,6 +1244,11 @@ impl HasResolver for DefWithBodyId {
             DefWithBodyId::StaticId(s) => s.resolver(db),
             DefWithBodyId::VariantId(v) => v.resolver(db),
             DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db),
+            DefWithBodyId::FieldId(f) => match f.parent {
+                VariantId::EnumVariantId(it) => it.resolver(db),
+                VariantId::StructId(it) => it.resolver(db),
+                VariantId::UnionId(it) => it.resolver(db),
+            },
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs
index 28b68121394..f8fb700d55e 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs
@@ -80,9 +80,15 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
     BuiltinDeriveExpander::find_by_name(ident)
 }
 
+#[derive(Clone, Copy)]
+enum HasDefault {
+    Yes,
+    No,
+}
+
 #[derive(Clone)]
 enum VariantShape {
-    Struct(Vec<tt::Ident>),
+    Struct(Vec<(tt::Ident, HasDefault)>),
     Tuple(usize),
     Unit,
 }
@@ -98,7 +104,7 @@ impl VariantShape {
 
     fn field_names(&self, span: Span) -> Vec<tt::Ident> {
         match self {
-            VariantShape::Struct(s) => s.clone(),
+            VariantShape::Struct(s) => s.iter().map(|(ident, _)| ident.clone()).collect(),
             VariantShape::Tuple(n) => tuple_field_iterator(span, *n).collect(),
             VariantShape::Unit => vec![],
         }
@@ -112,7 +118,7 @@ impl VariantShape {
     ) -> tt::TopSubtree {
         match self {
             VariantShape::Struct(fields) => {
-                let fields = fields.iter().map(|it| {
+                let fields = fields.iter().map(|(it, _)| {
                     let mapped = field_map(it);
                     quote! {span => #it : #mapped , }
                 });
@@ -135,6 +141,63 @@ impl VariantShape {
         }
     }
 
+    fn default_expand(
+        &self,
+        path: tt::TopSubtree,
+        span: Span,
+        field_map: impl Fn(&tt::Ident) -> tt::TopSubtree,
+    ) -> tt::TopSubtree {
+        match self {
+            VariantShape::Struct(fields) => {
+                let contains_default = fields.iter().any(|it| matches!(it.1, HasDefault::Yes));
+                let fields = fields
+                    .iter()
+                    .filter_map(|(it, has_default)| match has_default {
+                        HasDefault::Yes => None,
+                        HasDefault::No => Some(it),
+                    })
+                    .map(|it| {
+                        let mapped = field_map(it);
+                        quote! {span => #it : #mapped , }
+                    });
+                if contains_default {
+                    let mut double_dots =
+                        tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(span));
+                    double_dots.push(tt::Leaf::Punct(tt::Punct {
+                        char: '.',
+                        spacing: tt::Spacing::Joint,
+                        span,
+                    }));
+                    double_dots.push(tt::Leaf::Punct(tt::Punct {
+                        char: '.',
+                        spacing: tt::Spacing::Alone,
+                        span,
+                    }));
+                    let double_dots = double_dots.build();
+                    quote! {span =>
+                        #path { ##fields #double_dots }
+                    }
+                } else {
+                    quote! {span =>
+                        #path { ##fields }
+                    }
+                }
+            }
+            &VariantShape::Tuple(n) => {
+                let fields = tuple_field_iterator(span, n).map(|it| {
+                    let mapped = field_map(&it);
+                    quote! {span =>
+                        #mapped ,
+                    }
+                });
+                quote! {span =>
+                    #path ( ##fields )
+                }
+            }
+            VariantShape::Unit => path,
+        }
+    }
+
     fn from(
         call_site: Span,
         tm: &ExpansionSpanMap,
@@ -144,8 +207,15 @@ impl VariantShape {
             None => VariantShape::Unit,
             Some(FieldList::RecordFieldList(it)) => VariantShape::Struct(
                 it.fields()
-                    .map(|it| it.name())
-                    .map(|it| name_to_token(call_site, tm, it))
+                    .map(|it| {
+                        (
+                            it.name(),
+                            if it.expr().is_some() { HasDefault::Yes } else { HasDefault::No },
+                        )
+                    })
+                    .map(|(it, has_default)| {
+                        name_to_token(call_site, tm, it).map(|ident| (ident, has_default))
+                    })
                     .collect::<Result<_, _>>()?,
             ),
             Some(FieldList::TupleFieldList(it)) => VariantShape::Tuple(it.fields().count()),
@@ -601,7 +671,7 @@ fn default_expand(
         let body = match &adt.shape {
             AdtShape::Struct(fields) => {
                 let name = &adt.name;
-                fields.as_pattern_map(
+                fields.default_expand(
                     quote!(span =>#name),
                     span,
                     |_| quote!(span =>#krate::default::Default::default()),
@@ -611,7 +681,7 @@ fn default_expand(
                 if let Some(d) = default_variant {
                     let (name, fields) = &variants[*d];
                     let adt_name = &adt.name;
-                    fields.as_pattern_map(
+                    fields.default_expand(
                         quote!(span =>#adt_name :: #name),
                         span,
                         |_| quote!(span =>#krate::default::Default::default()),
@@ -643,7 +713,7 @@ fn debug_expand(
     expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, |adt| {
         let for_variant = |name: String, v: &VariantShape| match v {
             VariantShape::Struct(fields) => {
-                let for_fields = fields.iter().map(|it| {
+                let for_fields = fields.iter().map(|(it, _)| {
                     let x_string = it.to_string();
                     quote! {span =>
                         .field(#x_string, & #it)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 99ec1951a37..03218b4691b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -33,7 +33,8 @@ pub fn missing_unsafe(
         DefWithBodyId::StaticId(_)
         | DefWithBodyId::ConstId(_)
         | DefWithBodyId::VariantId(_)
-        | DefWithBodyId::InTypeConstId(_) => false,
+        | DefWithBodyId::InTypeConstId(_)
+        | DefWithBodyId::FieldId(_) => false,
     };
 
     let body = db.body(def);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 3c258e3c4cf..1b2ef2aef3f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -134,6 +134,9 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
                 .unwrap()
                 .0;
         }
+        DefWithBodyId::FieldId(f) => {
+            ctx.collect_field(f);
+        }
     }
 
     ctx.infer_body();
@@ -910,6 +913,19 @@ impl<'a> InferenceContext<'a> {
         self.return_ty = return_ty;
     }
 
+    fn collect_field(&mut self, field: FieldId) {
+        let variant_data = field.parent.variant_data(self.db.upcast());
+        let field_data = &variant_data.fields()[field.local_id];
+        let types_map = variant_data.types_map();
+        let return_ty =
+            self.make_ty(field_data.type_ref, types_map, InferenceTyDiagnosticSource::Signature);
+
+        // Field default value exprs might be defining usage sites of TAITs.
+        self.make_tait_coercion_table(iter::once(&return_ty));
+
+        self.return_ty = return_ty;
+    }
+
     fn collect_fn(&mut self, func: FunctionId) {
         let data = self.db.function_data(func);
         let mut param_tys =
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index cc6ed122af4..23072011a7b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -2130,6 +2130,10 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<Mi
             db.enum_variant_data(it).name.display(db.upcast(), edition).to_string()
         }
         DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"),
+        DefWithBodyId::FieldId(it) => it.parent.variant_data(db.upcast()).fields()[it.local_id]
+            .name
+            .display(db.upcast(), edition)
+            .to_string(),
     };
     let _p = tracing::info_span!("mir_body_query", ?detail).entered();
     let body = db.body(def);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index 2a26101ac43..193b7bcd97c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -6,7 +6,7 @@ use std::{
 };
 
 use either::Either;
-use hir_def::{expr_store::Body, hir::BindingId};
+use hir_def::{expr_store::Body, hir::BindingId, VariantId};
 use hir_expand::{name::Name, Lookup};
 use la_arena::ArenaMap;
 use span::Edition;
@@ -79,6 +79,38 @@ impl MirBody {
             hir_def::DefWithBodyId::InTypeConstId(id) => {
                 w!(this, "in type const {id:?} = ");
             }
+            hir_def::DefWithBodyId::FieldId(id) => {
+                w!(this, "field ");
+                match id.parent {
+                    VariantId::EnumVariantId(it) => {
+                        let loc = it.lookup(db.upcast());
+                        let enum_loc = loc.parent.lookup(db.upcast());
+                        w!(
+                            this,
+                            "{}::{}",
+                            enum_loc.id.item_tree(db.upcast())[enum_loc.id.value]
+                                .name
+                                .display(db.upcast(), Edition::LATEST),
+                            loc.id.item_tree(db.upcast())[loc.id.value]
+                                .name
+                                .display(db.upcast(), Edition::LATEST),
+                        );
+                    }
+                    VariantId::StructId(id) => {
+                        id.lookup(db.upcast()).id.resolved(db.upcast(), |it| {
+                            w!(this, "{}", it.name.display(db.upcast(), Edition::LATEST));
+                        });
+                    }
+                    VariantId::UnionId(id) => {
+                        id.lookup(db.upcast()).id.resolved(db.upcast(), |it| {
+                            w!(this, "{}", it.name.display(db.upcast(), Edition::LATEST));
+                        });
+                    }
+                };
+                let variant_data = id.parent.variant_data(db.upcast());
+                let field_name = &variant_data.fields()[id.local_id].name;
+                w!(this, ".{}: _ = ", field_name.display(db.upcast(), Edition::LATEST));
+            }
         });
         ctx.result
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 69ec35f406d..a5af712b42f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -160,6 +160,7 @@ fn check_impl(
             loc.source(&db).value.syntax().text_range().start()
         }
         DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
+        DefWithBodyId::FieldId(_) => unreachable!(),
     });
     let mut unexpected_type_mismatches = String::new();
     for def in defs {
@@ -415,6 +416,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
             loc.source(&db).value.syntax().text_range().start()
         }
         DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
+        DefWithBodyId::FieldId(_) => unreachable!(),
     });
     for def in defs {
         let (body, source_map) = db.body_with_source_map(def);
diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
index 537401afdc3..dd26e894d79 100644
--- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
@@ -147,6 +147,7 @@ impl From<DefWithBody> for DefWithBodyId {
             DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
             DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
             DefWithBody::InTypeConst(it) => DefWithBodyId::InTypeConstId(it.id),
+            DefWithBody::Field(it) => DefWithBodyId::FieldId(it.into()),
         }
     }
 }
@@ -159,6 +160,7 @@ impl From<DefWithBodyId> for DefWithBody {
             DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()),
             DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()),
             DefWithBodyId::InTypeConstId(it) => DefWithBody::InTypeConst(it.into()),
+            DefWithBodyId::FieldId(it) => DefWithBody::Field(it.into()),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 27723cbc16a..8a3aa140474 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -415,6 +415,19 @@ impl ModuleDef {
             def.diagnostics(db, &mut acc);
         }
 
+        let fields = match self {
+            ModuleDef::Adt(Adt::Struct(it)) => Some(it.fields(db)),
+            ModuleDef::Adt(Adt::Union(it)) => Some(it.fields(db)),
+            ModuleDef::Variant(it) => Some(it.fields(db)),
+            _ => None,
+        };
+        if let Some(fields) = fields {
+            for field in fields {
+                let def: DefWithBody = field.into();
+                def.diagnostics(db, &mut acc, style_lints);
+            }
+        }
+
         acc
     }
 
@@ -1226,6 +1239,12 @@ impl HasVisibility for Module {
     }
 }
 
+impl From<&Field> for DefWithBodyId {
+    fn from(&f: &Field) -> Self {
+        DefWithBodyId::FieldId(f.into())
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Field {
     pub(crate) parent: VariantDef,
@@ -1291,6 +1310,10 @@ impl AstNode for FieldSource {
 }
 
 impl Field {
+    pub fn module(self, db: &dyn HirDatabase) -> Module {
+        self.parent.module(db)
+    }
+
     pub fn name(&self, db: &dyn HirDatabase) -> Name {
         self.parent.variant_data(db).fields()[self.id].name.clone()
     }
@@ -1353,6 +1376,14 @@ impl Field {
     pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
         self.parent
     }
+
+    pub fn default_value_source(
+        &self,
+        db: &dyn HirDatabase,
+    ) -> Option<InFileWrapper<HirFileId, ast::Expr>> {
+        let id: hir_def::FieldId = (*self).into();
+        id.record_field_source(db.upcast()).map(|it| it.and_then(|it| it.expr())).transpose()
+    }
 }
 
 impl HasVisibility for Field {
@@ -1789,8 +1820,9 @@ pub enum DefWithBody {
     Const(Const),
     Variant(Variant),
     InTypeConst(InTypeConst),
+    Field(Field),
 }
-impl_from!(Function, Const, Static, Variant, InTypeConst for DefWithBody);
+impl_from!(Function, Const, Static, Variant, InTypeConst, Field for DefWithBody);
 
 impl DefWithBody {
     pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -1800,6 +1832,7 @@ impl DefWithBody {
             DefWithBody::Static(s) => s.module(db),
             DefWithBody::Variant(v) => v.module(db),
             DefWithBody::InTypeConst(c) => c.module(db),
+            DefWithBody::Field(f) => f.module(db),
         }
     }
 
@@ -1810,6 +1843,7 @@ impl DefWithBody {
             DefWithBody::Const(c) => c.name(db),
             DefWithBody::Variant(v) => Some(v.name(db)),
             DefWithBody::InTypeConst(_) => None,
+            DefWithBody::Field(f) => Some(f.name(db)),
         }
     }
 
@@ -1825,6 +1859,7 @@ impl DefWithBody {
                 &DefWithBodyId::from(it.id).resolver(db.upcast()),
                 TyKind::Error.intern(Interner),
             ),
+            DefWithBody::Field(it) => it.ty(db),
         }
     }
 
@@ -1835,6 +1870,7 @@ impl DefWithBody {
             DefWithBody::Const(it) => it.id.into(),
             DefWithBody::Variant(it) => it.into(),
             DefWithBody::InTypeConst(it) => it.id.into(),
+            DefWithBody::Field(it) => it.into(),
         }
     }
 
@@ -1880,6 +1916,23 @@ impl DefWithBody {
                 item_tree_source_maps.konst(konst.value)
             }
             DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => &TypesSourceMap::EMPTY,
+            DefWithBody::Field(field) => match field.parent {
+                VariantDef::Struct(strukt) => {
+                    let strukt = strukt.id.lookup(db.upcast()).id;
+                    item_tree_source_maps = strukt.item_tree_with_source_map(db.upcast()).1;
+                    item_tree_source_maps.strukt(strukt.value).item()
+                }
+                VariantDef::Union(union) => {
+                    let union = union.id.lookup(db.upcast()).id;
+                    item_tree_source_maps = union.item_tree_with_source_map(db.upcast()).1;
+                    item_tree_source_maps.union(union.value).item()
+                }
+                VariantDef::Variant(variant) => {
+                    let variant = variant.id.lookup(db.upcast()).id;
+                    item_tree_source_maps = variant.item_tree_with_source_map(db.upcast()).1;
+                    item_tree_source_maps.variant(variant.value)
+                }
+            },
         };
 
         for (_, def_map) in body.blocks(db.upcast()) {
@@ -2111,8 +2164,8 @@ impl DefWithBody {
             DefWithBody::Static(it) => it.into(),
             DefWithBody::Const(it) => it.into(),
             DefWithBody::Variant(it) => it.into(),
-            // FIXME: don't ignore diagnostics for in type const
-            DefWithBody::InTypeConst(_) => return,
+            // FIXME: don't ignore diagnostics for in type const and default field value exprs
+            DefWithBody::InTypeConst(_) | DefWithBody::Field(_) => return,
         };
         for diag in hir_ty::diagnostics::incorrect_case(db, def.into()) {
             acc.push(diag.into())
@@ -3237,7 +3290,10 @@ impl AsAssocItem for DefWithBody {
         match self {
             DefWithBody::Function(it) => it.as_assoc_item(db),
             DefWithBody::Const(it) => it.as_assoc_item(db),
-            DefWithBody::Static(_) | DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => None,
+            DefWithBody::Static(_)
+            | DefWithBody::Variant(_)
+            | DefWithBody::InTypeConst(_)
+            | DefWithBody::Field(_) => None,
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index bad53608056..6925880ba99 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -972,6 +972,7 @@ impl TryFrom<DefWithBody> for Definition {
             DefWithBody::Const(it) => Ok(it.into()),
             DefWithBody::Variant(it) => Ok(it.into()),
             DefWithBody::InTypeConst(_) => Err(()),
+            DefWithBody::Field(it) => Ok(it.into()),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 7963e8ae4f7..d2a237a5c00 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -310,6 +310,9 @@ impl Definition {
                 DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
                 // FIXME: implement
                 DefWithBody::InTypeConst(_) => return SearchScope::empty(),
+                DefWithBody::Field(f) => {
+                    f.default_value_source(db).map(|src| src.syntax().cloned())
+                }
             };
             return match def {
                 Some(def) => SearchScope::file_range(
@@ -327,6 +330,9 @@ impl Definition {
                 DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
                 // FIXME: implement
                 DefWithBody::InTypeConst(_) => return SearchScope::empty(),
+                DefWithBody::Field(f) => {
+                    f.default_value_source(db).map(|src| src.syntax().cloned())
+                }
             };
             return match def {
                 Some(def) => SearchScope::file_range(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 56afb38cc81..7bc1be38226 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1235,4 +1235,19 @@ fn f() {
 "#,
         );
     }
+
+    #[test]
+    fn diagnostics_inside_field_default_expr() {
+        check_diagnostics(
+            r#"
+struct Foo {
+    foo: i32 = {
+        let x = false;
+        x
+     // ^ error: expected i32, found bool
+    },
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index 389c01933c9..fe1316c9bfd 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -678,6 +678,8 @@ fn path_expr(p: &mut Parser<'_>, r: Restrictions) -> (CompletedMarker, BlockLike
 //     S { x };
 //     S { x, y: 32, };
 //     S { x, y: 32, ..Default::default() };
+//     S { x, y: 32, .. };
+//     S { .. };
 //     S { x: ::default() };
 //     TupleStruct { 0: 1 };
 // }
@@ -709,6 +711,8 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
                 // fn main() {
                 //     S { field ..S::default() }
                 //     S { 0 ..S::default() }
+                //     S { field .. }
+                //     S { 0 .. }
                 // }
                 name_ref_or_index(p);
                 p.error("expected `:`");
@@ -739,7 +743,13 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
                 //     S { .. } = S {};
                 // }
 
-                // We permit `.. }` on the left-hand side of a destructuring assignment.
+                // test struct_initializer_with_defaults
+                // fn foo() {
+                //     let _s = S { .. };
+                // }
+
+                // We permit `.. }` on the left-hand side of a destructuring assignment
+                // or defaults values.
                 if !p.at(T!['}']) {
                     expr(p);
 
@@ -750,6 +760,12 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
                         //     S { ..x, a: 0 }
                         // }
 
+                        // test_err comma_after_default_values_syntax
+                        // fn foo() {
+                        //     S { .., };
+                        //     S { .., a: 0 }
+                        // }
+
                         // Do not bump, so we can support additional fields after this comma.
                         p.error("cannot use a comma after the base struct");
                     }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs
index 21078175c0e..9a16c9db6da 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs
@@ -135,6 +135,11 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) {
             name(p);
             p.expect(T![:]);
             types::type_(p);
+            // test record_field_default_values
+            // struct S { f: f32 = 0.0 }
+            if p.eat(T![=]) {
+                expressions::expr(p);
+            }
             m.complete(p, RECORD_FIELD);
         } else {
             m.abandon(p);
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
index b9f87b6af24..c8ea8c547a9 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -482,6 +482,10 @@ mod ok {
         run_and_expect_no_errors("test_data/parser/inline/ok/record_field_attrs.rs");
     }
     #[test]
+    fn record_field_default_values() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/record_field_default_values.rs");
+    }
+    #[test]
     fn record_field_list() {
         run_and_expect_no_errors("test_data/parser/inline/ok/record_field_list.rs");
     }
@@ -544,6 +548,10 @@ mod ok {
         run_and_expect_no_errors("test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs");
     }
     #[test]
+    fn struct_initializer_with_defaults() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/struct_initializer_with_defaults.rs");
+    }
+    #[test]
     fn struct_item() { run_and_expect_no_errors("test_data/parser/inline/ok/struct_item.rs"); }
     #[test]
     fn trait_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/trait_alias.rs"); }
@@ -719,6 +727,10 @@ mod err {
         );
     }
     #[test]
+    fn comma_after_default_values_syntax() {
+        run_and_expect_errors("test_data/parser/inline/err/comma_after_default_values_syntax.rs");
+    }
+    #[test]
     fn crate_visibility_empty_recover() {
         run_and_expect_errors("test_data/parser/inline/err/crate_visibility_empty_recover.rs");
     }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast
new file mode 100644
index 00000000000..feb617e1aa2
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast
@@ -0,0 +1,59 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              DOT2 ".."
+              ERROR
+                COMMA ","
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        RECORD_EXPR
+          PATH
+            PATH_SEGMENT
+              NAME_REF
+                IDENT "S"
+          WHITESPACE " "
+          RECORD_EXPR_FIELD_LIST
+            L_CURLY "{"
+            WHITESPACE " "
+            DOT2 ".."
+            ERROR
+              COMMA ","
+            WHITESPACE " "
+            RECORD_EXPR_FIELD
+              NAME_REF
+                IDENT "a"
+              COLON ":"
+              WHITESPACE " "
+              LITERAL
+                INT_NUMBER "0"
+            WHITESPACE " "
+            R_CURLY "}"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 21: expected expression
+error 36: expected expression
+error 37: expected COMMA
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs
new file mode 100644
index 00000000000..f1ecdf89fab
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs
@@ -0,0 +1,4 @@
+fn foo() {
+    S { .., };
+    S { .., a: 0 }
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
index 08ae906421c..12b4e233e30 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
@@ -44,6 +44,56 @@ SOURCE_FILE
               WHITESPACE " "
               R_CURLY "}"
         WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  INT_NUMBER "0"
+              WHITESPACE " "
+              DOT2 ".."
+              CALL_EXPR
+                PATH_EXPR
+                  PATH
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "S"
+                    COLON2 "::"
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "default"
+                ARG_LIST
+                  L_PAREN "("
+                  R_PAREN ")"
+              WHITESPACE " "
+              R_CURLY "}"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  IDENT "field"
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+        WHITESPACE "\n    "
         RECORD_EXPR
           PATH
             PATH_SEGMENT
@@ -58,20 +108,6 @@ SOURCE_FILE
                 INT_NUMBER "0"
             WHITESPACE " "
             DOT2 ".."
-            CALL_EXPR
-              PATH_EXPR
-                PATH
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "S"
-                  COLON2 "::"
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "default"
-              ARG_LIST
-                L_PAREN "("
-                R_PAREN ")"
             WHITESPACE " "
             R_CURLY "}"
         WHITESPACE "\n"
@@ -82,3 +118,9 @@ error 25: expected COMMA
 error 42: expected SEMICOLON
 error 52: expected `:`
 error 52: expected COMMA
+error 69: expected SEMICOLON
+error 83: expected `:`
+error 83: expected COMMA
+error 88: expected SEMICOLON
+error 98: expected `:`
+error 98: expected COMMA
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
index 65398ccb88e..416cd763fdb 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
@@ -1,4 +1,6 @@
 fn main() {
     S { field ..S::default() }
     S { 0 ..S::default() }
+    S { field .. }
+    S { 0 .. }
 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast
new file mode 100644
index 00000000000..33088f2cabf
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast
@@ -0,0 +1,28 @@
+SOURCE_FILE
+  STRUCT
+    STRUCT_KW "struct"
+    WHITESPACE " "
+    NAME
+      IDENT "S"
+    WHITESPACE " "
+    RECORD_FIELD_LIST
+      L_CURLY "{"
+      WHITESPACE " "
+      RECORD_FIELD
+        NAME
+          IDENT "f"
+        COLON ":"
+        WHITESPACE " "
+        PATH_TYPE
+          PATH
+            PATH_SEGMENT
+              NAME_REF
+                IDENT "f32"
+        WHITESPACE " "
+        EQ "="
+        WHITESPACE " "
+        LITERAL
+          FLOAT_NUMBER "0.0"
+      WHITESPACE " "
+      R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs
new file mode 100644
index 00000000000..d7b38944a8a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs
@@ -0,0 +1 @@
+struct S { f: f32 = 0.0 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
index 00948c322f4..b868da55bce 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
@@ -131,6 +131,53 @@ SOURCE_FILE
               L_CURLY "{"
               WHITESPACE " "
               RECORD_EXPR_FIELD
+                PATH_EXPR
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "x"
+              COMMA ","
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  IDENT "y"
+                COLON ":"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "32"
+              COMMA ","
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
                 NAME_REF
                   IDENT "x"
                 COLON ":"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
index 86411fbb7dc..42895f759b2 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
@@ -3,6 +3,8 @@ fn foo() {
     S { x };
     S { x, y: 32, };
     S { x, y: 32, ..Default::default() };
+    S { x, y: 32, .. };
+    S { .. };
     S { x: ::default() };
     TupleStruct { 0: 1 };
 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast
new file mode 100644
index 00000000000..987e219ae82
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast
@@ -0,0 +1,39 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "_s"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs
new file mode 100644
index 00000000000..e08204f94c4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs
@@ -0,0 +1,3 @@
+fn foo() {
+    let _s = S { .. };
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index cd709afe091..824f262ca36 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -673,6 +673,9 @@ impl flags::AnalysisStats {
                         DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()),
                         DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()),
                         DefWithBody::InTypeConst(_) => unimplemented!(),
+                        DefWithBody::Field(it) => {
+                            it.default_value_source(db).map(|it| it.syntax().cloned())
+                        }
                     };
                     if let Some(src) = source {
                         let original_file = src.file_id.original_file(db);
@@ -987,6 +990,9 @@ impl flags::AnalysisStats {
                         DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()),
                         DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()),
                         DefWithBody::InTypeConst(_) => unimplemented!(),
+                        DefWithBody::Field(it) => {
+                            it.default_value_source(db).map(|it| it.syntax().cloned())
+                        }
                     };
                     if let Some(src) = source {
                         let original_file = src.file_id.original_file(db);
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 4e2a70d6cd9..bbb8413cbc0 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -241,7 +241,7 @@ RecordFieldList =
 
 RecordField =
   Attr* Visibility?
-  Name ':' Type
+  Name ':' Type ('=' Expr)?
 
 TupleFieldList =
   '(' fields:(TupleField (',' TupleField)* ','?)? ')'
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 69e2a9f9c1b..8f10ea94645 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -1539,9 +1539,13 @@ impl ast::HasName for RecordField {}
 impl ast::HasVisibility for RecordField {}
 impl RecordField {
     #[inline]
+    pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+    #[inline]
     pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
     #[inline]
     pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
+    #[inline]
+    pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]