diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index 8fc19854033c..aaa260a358b6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -85,6 +85,7 @@ pub struct FieldData { pub name: Name, pub type_ref: TypeRefId, pub visibility: RawVisibility, + pub has_default: bool, } fn repr_from_value( @@ -478,5 +479,6 @@ fn lower_field( name: field.name.clone(), type_ref: field.type_ref, visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(), + has_default: field.has_default, } } 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 0d3a542a4393..65580bce4dda 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 @@ -603,9 +603,10 @@ impl ExprCollector<'_> { }) .collect(); let spread = nfl.spread().map(|s| self.collect_expr(s)); - Expr::RecordLit { path, fields, spread } + let ellipsis = nfl.dotdot_token().is_some(); + Expr::RecordLit { path, fields, spread, ellipsis } } else { - Expr::RecordLit { path, fields: Box::default(), spread: None } + Expr::RecordLit { path, fields: Box::default(), spread: None, ellipsis: false } }; self.alloc_expr(record_lit, syntax_ptr) 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 6ba0bbd61c48..9a8a8c2cd057 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 @@ -398,7 +398,7 @@ impl Printer<'_> { self.print_expr(*expr); } } - Expr::RecordLit { path, fields, spread } => { + Expr::RecordLit { path, fields, spread, ellipsis: _ } => { match path { Some(path) => self.print_path(path), None => w!(self, "�"), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 0dcddf162b2f..1e2417ecdf3a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -252,6 +252,7 @@ pub enum Expr { path: Option>, fields: Box<[RecordLitField]>, spread: Option, + ellipsis: bool, }, Field { expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 79ee3344d256..09fb5f6fd2d3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -1006,6 +1006,7 @@ pub struct Field { pub name: Name, pub type_ref: TypeRefId, pub visibility: RawVisibilityId, + pub has_default: bool, } #[derive(Debug, Clone, Eq, PartialEq)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 71848845a84d..69a1933079ce 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -319,8 +319,9 @@ impl<'a> Ctx<'a> { }; let visibility = self.lower_visibility(field); let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty()); + let has_default = field.expr().is_some(); - Field { name, type_ref, visibility } + Field { name, type_ref, visibility, has_default } } fn lower_tuple_field( @@ -332,7 +333,7 @@ impl<'a> Ctx<'a> { let name = Name::new_tuple_field(idx); let visibility = self.lower_visibility(field); let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty()); - Field { name, type_ref, visibility } + Field { name, type_ref, visibility, has_default: false } } fn lower_union(&mut self, union: &ast::Union) -> Option> { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 70bf2f13c88a..1e765ac78eb1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -135,7 +135,9 @@ impl Printer<'_> { self.whitespace(); w!(self, "{{"); self.indented(|this| { - for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() { + for (idx, Field { name, type_ref, visibility, has_default: _ }) in + fields.iter().enumerate() + { this.print_attrs_of( AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))), "\n", @@ -151,7 +153,9 @@ impl Printer<'_> { FieldsShape::Tuple => { w!(self, "("); self.indented(|this| { - for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() { + for (idx, Field { name, type_ref, visibility, has_default: _ }) in + fields.iter().enumerate() + { this.print_attrs_of( AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))), "\n", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 0b5f1319243f..d8700e277774 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -547,8 +547,8 @@ pub fn record_literal_missing_fields( id: ExprId, expr: &Expr, ) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> { - let (fields, exhaustive) = match expr { - Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()), + let (fields, exhaustive, ellipsis) = match expr { + Expr::RecordLit { fields, spread, ellipsis, .. } => (fields, spread.is_none(), *ellipsis), _ => return None, }; @@ -563,7 +563,13 @@ pub fn record_literal_missing_fields( let missed_fields: Vec = variant_data .fields() .iter() - .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) }) + .filter_map(|(f, d)| { + if (ellipsis && d.has_default) || specified_fields.contains(&d.name) { + None + } else { + Some(f) + } + }) .collect(); if missed_fields.is_empty() { return None; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index d74a383f44ef..5b6c3cd15244 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -121,7 +121,7 @@ impl InferenceContext<'_> { Expr::Become { expr } => { self.infer_mut_expr(*expr, Mutability::Not); } - Expr::RecordLit { path: _, fields, spread } => { + Expr::RecordLit { path: _, fields, spread, ellipsis: _ } => { self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) } &Expr::Index { base, index } => { 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 23072011a7b3..85e8d1720310 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 @@ -823,7 +823,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Become { .. } => not_supported!("tail-calls"), Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, spread } => { + Expr::RecordLit { fields, path, spread, ellipsis: _ } => { let spread_place = match spread { &Some(it) => { let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 938b7182bc94..c495df9daaaf 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -846,4 +846,35 @@ pub struct Claims { "#, ); } + + #[test] + fn default_field_values() { + check_diagnostics( + r#" +struct F { + field1: i32 = 4, + field2: bool, +} + +fn f() { + let _f = F { + field2: true, + .. + }; + + let _f = F { + //^ 💡 error: missing structure fields: + //| - field1 + field2: true, + }; + + let _f = F { + //^ 💡 error: missing structure fields: + //| - field2 + .. + }; +} +"#, + ); + } }