Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions crates/hir-def/src/expr_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::{
expr_store::path::Path,
hir::{
Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat,
PatId, RecordFieldPat, Statement,
PatId, RecordFieldPat, RecordSpread, Statement,
},
nameres::{DefMap, block_def_map},
type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId},
Expand Down Expand Up @@ -575,8 +575,8 @@ impl ExpressionStore {
for field in fields.iter() {
f(field.expr);
}
if let &Some(expr) = spread {
f(expr);
if let RecordSpread::Expr(expr) = spread {
f(*expr);
}
}
Expr::Closure { body, .. } => {
Expand Down Expand Up @@ -706,8 +706,8 @@ impl ExpressionStore {
for field in fields.iter() {
f(field.expr);
}
if let &Some(expr) = spread {
f(expr);
if let RecordSpread::Expr(expr) = spread {
f(*expr);
}
}
Expr::Closure { body, .. } => {
Expand Down
14 changes: 10 additions & 4 deletions crates/hir-def/src/expr_store/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use crate::{
hir::{
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId,
RecordFieldPat, RecordLitField, Statement, generics::GenericParams,
RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams,
},
item_scope::BuiltinShadowMode,
item_tree::FieldsShape,
Expand Down Expand Up @@ -1232,10 +1232,16 @@ impl<'db> ExprCollector<'db> {
Some(RecordLitField { name, expr })
})
.collect();
let spread = nfl.spread().map(|s| self.collect_expr(s));
let spread_expr = nfl.spread().map(|s| self.collect_expr(s));
let has_spread_syntax = nfl.dotdot_token().is_some();
let spread = match (spread_expr, has_spread_syntax) {
(None, false) => RecordSpread::None,
(None, true) => RecordSpread::FieldDefaults,
(Some(expr), _) => RecordSpread::Expr(expr),
};
Expr::RecordLit { path, fields, spread }
} else {
Expr::RecordLit { path, fields: Box::default(), spread: None }
Expr::RecordLit { path, fields: Box::default(), spread: RecordSpread::None }
};

self.alloc_expr(record_lit, syntax_ptr)
Expand Down Expand Up @@ -1961,7 +1967,7 @@ impl<'db> ExprCollector<'db> {
}
}

fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
pub fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
match expr {
Some(expr) => self.collect_expr(expr),
None => self.missing_expr(),
Expand Down
5 changes: 3 additions & 2 deletions crates/hir-def/src/expr_store/lower/format_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use crate::{
builtin_type::BuiltinUint,
expr_store::{HygieneId, lower::ExprCollector, path::Path},
hir::{
Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, Statement,
Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, RecordSpread,
Statement,
format_args::{
self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
Expand Down Expand Up @@ -869,7 +870,7 @@ impl<'db> ExprCollector<'db> {
self.alloc_expr_desugared(Expr::RecordLit {
path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new),
fields: Box::new([position, flags, precision, width]),
spread: None,
spread: RecordSpread::None,
})
} else {
let format_placeholder_new =
Expand Down
20 changes: 14 additions & 6 deletions crates/hir-def/src/expr_store/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ use crate::{
attrs::AttrFlags,
expr_store::path::{GenericArg, GenericArgs},
hir::{
Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement,
Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, RecordSpread,
Statement,
generics::{GenericParams, WherePredicate},
},
lang_item::LangItemTarget,
Expand Down Expand Up @@ -139,7 +140,7 @@ pub fn print_variant_body_hir(db: &dyn DefDatabase, owner: VariantId, edition: E
}

for (_, data) in fields.fields().iter() {
let FieldData { name, type_ref, visibility, is_unsafe } = data;
let FieldData { name, type_ref, visibility, is_unsafe, default_value: _ } = data;
match visibility {
crate::item_tree::RawVisibility::Module(interned, _visibility_explicitness) => {
w!(p, "pub(in {})", interned.display(db, p.edition))
Expand Down Expand Up @@ -679,10 +680,17 @@ impl Printer<'_> {
p.print_expr(field.expr);
wln!(p, ",");
}
if let Some(spread) = spread {
w!(p, "..");
p.print_expr(*spread);
wln!(p);
match spread {
RecordSpread::None => {}
RecordSpread::FieldDefaults => {
w!(p, "..");
wln!(p);
}
RecordSpread::Expr(spread_expr) => {
w!(p, "..");
p.print_expr(*spread_expr);
wln!(p);
}
}
});
w!(self, "}}");
Expand Down
9 changes: 8 additions & 1 deletion crates/hir-def/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ impl From<ast::LiteralKind> for Literal {
}
}

#[derive(Debug, Clone, Eq, PartialEq, Copy)]
pub enum RecordSpread {
None,
FieldDefaults,
Expr(ExprId),
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr {
/// This is produced if the syntax tree does not have a required expression piece.
Expand Down Expand Up @@ -259,7 +266,7 @@ pub enum Expr {
RecordLit {
path: Option<Box<Path>>,
fields: Box<[RecordLitField]>,
spread: Option<ExprId>,
spread: RecordSpread,
},
Field {
expr: ExprId,
Expand Down
12 changes: 10 additions & 2 deletions crates/hir-def/src/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use intern::{Symbol, sym};
use la_arena::{Arena, Idx};
use rustc_abi::{IntegerType, ReprOptions};
use syntax::{
NodeOrToken, SyntaxNodePtr, T,
AstNode, NodeOrToken, SyntaxNodePtr, T,
ast::{self, HasGenericParams, HasName, HasVisibility, IsString},
};
use thin_vec::ThinVec;
Expand Down Expand Up @@ -754,6 +754,7 @@ pub struct FieldData {
pub type_ref: TypeRefId,
pub visibility: RawVisibility,
pub is_unsafe: bool,
pub default_value: Option<ExprId>,
}

pub type LocalFieldId = Idx<FieldData>;
Expand Down Expand Up @@ -903,7 +904,14 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
.filter_map(NodeOrToken::into_token)
.any(|token| token.kind() == T![unsafe]);
let name = field_name(idx, &field);
arena.alloc(FieldData { name, type_ref, visibility, is_unsafe });

// Check if field has default value (only for record fields)
let default_value = ast::RecordField::cast(field.syntax().clone())
.and_then(|rf| rf.eq_token().is_some().then_some(rf.expr()))
.flatten()
.map(|expr| col.collect_expr_opt(Some(expr)));

arena.alloc(FieldData { name, type_ref, visibility, is_unsafe, default_value });
idx += 1;
}
Err(cfg) => {
Expand Down
46 changes: 33 additions & 13 deletions crates/hir-ty/src/diagnostics/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use crate::{
pub(crate) use hir_def::{
LocalFieldId, VariantId,
expr_store::Body,
hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement},
hir::{Expr, ExprId, MatchArm, Pat, PatId, RecordSpread, Statement},
};

pub enum BodyValidationDiagnostic {
Expand Down Expand Up @@ -123,7 +123,7 @@ impl<'db> ExprValidator<'db> {
}

for (id, expr) in body.exprs() {
if let Some((variant, missed_fields, true)) =
if let Some((variant, missed_fields)) =
record_literal_missing_fields(db, self.infer, id, expr)
{
self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
Expand Down Expand Up @@ -154,7 +154,7 @@ impl<'db> ExprValidator<'db> {
}

for (id, pat) in body.pats() {
if let Some((variant, missed_fields, true)) =
if let Some((variant, missed_fields)) =
record_pattern_missing_fields(db, self.infer, id, pat)
{
self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
Expand Down Expand Up @@ -557,9 +557,9 @@ pub fn record_literal_missing_fields(
infer: &InferenceResult,
id: ExprId,
expr: &Expr,
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
let (fields, exhaustive) = match expr {
Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()),
) -> Option<(VariantId, Vec<LocalFieldId>)> {
let (fields, spread) = match expr {
Expr::RecordLit { fields, spread, .. } => (fields, spread),
_ => return None,
};

Expand All @@ -571,25 +571,38 @@ pub fn record_literal_missing_fields(
let variant_data = variant_def.fields(db);

let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
// don't show missing fields if:
// - has ..expr
// - or has default value + ..
// - or already in code
let missed_fields: Vec<LocalFieldId> = variant_data
.fields()
.iter()
.filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) })
.filter_map(|(f, d)| {
if specified_fields.contains(&d.name)
|| matches!(spread, RecordSpread::Expr(_))
|| (d.default_value.is_some() && matches!(spread, RecordSpread::FieldDefaults))
{
None
} else {
Some(f)
}
})
.collect();
if missed_fields.is_empty() {
return None;
}
Some((variant_def, missed_fields, exhaustive))
Some((variant_def, missed_fields))
}

pub fn record_pattern_missing_fields(
db: &dyn HirDatabase,
infer: &InferenceResult,
id: PatId,
pat: &Pat,
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
let (fields, exhaustive) = match pat {
Pat::Record { path: _, args, ellipsis } => (args, !ellipsis),
) -> Option<(VariantId, Vec<LocalFieldId>)> {
let (fields, ellipsis) = match pat {
Pat::Record { path: _, args, ellipsis } => (args, *ellipsis),
_ => return None,
};

Expand All @@ -601,15 +614,22 @@ pub fn record_pattern_missing_fields(
let variant_data = variant_def.fields(db);

let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
// don't show missing fields if:
// - in code
// - or has ..
let missed_fields: Vec<LocalFieldId> = variant_data
.fields()
.iter()
.filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) })
.filter_map(
|(f, d)| {
if specified_fields.contains(&d.name) || ellipsis { None } else { Some(f) }
},
)
.collect();
if missed_fields.is_empty() {
return None;
}
Some((variant_def, missed_fields, exhaustive))
Some((variant_def, missed_fields))
}

fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool {
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-ty/src/infer/closure/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use hir_def::{
expr_store::path::Path,
hir::{
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
Statement, UnaryOp,
RecordSpread, Statement, UnaryOp,
},
item_tree::FieldsShape,
resolver::ValueNs,
Expand Down Expand Up @@ -627,7 +627,7 @@ impl<'db> InferenceContext<'_, 'db> {
self.consume_expr(expr);
}
Expr::RecordLit { fields, spread, .. } => {
if let &Some(expr) = spread {
if let RecordSpread::Expr(expr) = *spread {
self.consume_expr(expr);
}
self.consume_exprs(fields.iter().map(|it| it.expr));
Expand Down
6 changes: 3 additions & 3 deletions crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use hir_def::{
expr_store::path::{GenericArgs as HirGenericArgs, Path},
hir::{
Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId,
LabelId, Literal, Pat, PatId, Statement, UnaryOp,
LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp,
},
resolver::ValueNs,
};
Expand Down Expand Up @@ -657,8 +657,8 @@ impl<'db> InferenceContext<'_, 'db> {
}
}
}
if let Some(expr) = spread {
self.infer_expr(*expr, &Expectation::has_type(ty), ExprIsRead::Yes);
if let RecordSpread::Expr(expr) = *spread {
self.infer_expr(expr, &Expectation::has_type(ty), ExprIsRead::Yes);
}
ty
}
Expand Down
10 changes: 7 additions & 3 deletions crates/hir-ty/src/infer/mutability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.

use hir_def::hir::{
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, UnaryOp,
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, RecordSpread,
Statement, UnaryOp,
};
use rustc_ast_ir::Mutability;

Expand Down Expand Up @@ -132,8 +133,11 @@ impl<'db> InferenceContext<'_, 'db> {
Expr::Become { expr } => {
self.infer_mut_expr(*expr, Mutability::Not);
}
Expr::RecordLit { path: _, fields, spread } => {
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
Expr::RecordLit { path: _, fields, spread, .. } => {
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr));
if let RecordSpread::Expr(expr) = *spread {
self.infer_mut_expr(expr, Mutability::Not);
}
}
&Expr::Index { base, index } => {
if mutability == Mutability::Mut {
Expand Down
11 changes: 6 additions & 5 deletions crates/hir-ty/src/mir/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use hir_def::{
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
hir::{
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
Pat, PatId, RecordFieldPat, RecordLitField,
Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread,
},
item_tree::FieldsShape,
lang_item::LangItems,
Expand Down Expand Up @@ -867,16 +867,17 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
}
Expr::Become { .. } => not_supported!("tail-calls"),
Expr::Yield { .. } => not_supported!("yield"),
Expr::RecordLit { fields, path, spread } => {
let spread_place = match spread {
&Some(it) => {
Expr::RecordLit { fields, path, spread, .. } => {
let spread_place = match *spread {
RecordSpread::Expr(it) => {
let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {
return Ok(None);
};
current = c;
Some(p)
}
None => None,
RecordSpread::None => None,
RecordSpread::FieldDefaults => not_supported!("empty record spread"),
};
let variant_id =
self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path {
Expand Down
Loading