From 7de0b2e75a541b98f735ee6fcd12d326be38d23f Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 23 Jan 2025 00:27:31 +0900 Subject: [PATCH 1/8] feat: Implement `default-field-values` --- crates/hir-def/src/expr_store/body.rs | 3 + crates/hir-def/src/expr_store/lower.rs | 2 + crates/hir-def/src/expr_store/pretty.rs | 27 ++++++ crates/hir-def/src/lib.rs | 59 ++++++++++++- .../builtin_derive_macro.rs | 42 +++++++++ crates/hir-def/src/resolver.rs | 10 +++ crates/hir-expand/src/builtin/derive_macro.rs | 86 +++++++++++++++++-- crates/hir-ty/src/diagnostics/unsafe_check.rs | 3 +- crates/hir-ty/src/infer.rs | 16 ++++ crates/hir-ty/src/mir/lower.rs | 4 + crates/hir-ty/src/mir/pretty.rs | 34 +++++++- crates/hir-ty/src/tests.rs | 2 + crates/hir/src/from_id.rs | 2 + crates/hir/src/lib.rs | 64 +++++++++++++- crates/ide-db/src/defs.rs | 1 + crates/ide-db/src/search.rs | 6 ++ .../src/handlers/type_mismatch.rs | 15 ++++ crates/parser/src/grammar/expressions.rs | 18 +++- crates/parser/src/grammar/items/adt.rs | 5 ++ crates/parser/test_data/generated/runner.rs | 12 +++ .../comma_after_default_values_syntax.rast | 59 +++++++++++++ .../err/comma_after_default_values_syntax.rs | 4 + ...cord_literal_before_ellipsis_recovery.rast | 70 ++++++++++++--- ...record_literal_before_ellipsis_recovery.rs | 2 + .../ok/record_field_default_values.rast | 28 ++++++ .../inline/ok/record_field_default_values.rs | 1 + .../parser/inline/ok/record_lit.rast | 47 ++++++++++ .../test_data/parser/inline/ok/record_lit.rs | 2 + .../ok/struct_initializer_with_defaults.rast | 39 +++++++++ .../ok/struct_initializer_with_defaults.rs | 3 + .../rust-analyzer/src/cli/analysis_stats.rs | 6 ++ crates/syntax/rust.ungram | 2 +- crates/syntax/src/ast/generated/nodes.rs | 4 + 33 files changed, 647 insertions(+), 31 deletions(-) create mode 100644 crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast create mode 100644 crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs create mode 100644 crates/parser/test_data/parser/inline/ok/record_field_default_values.rast create mode 100644 crates/parser/test_data/parser/inline/ok/record_field_default_values.rs create mode 100644 crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast create mode 100644 crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs diff --git a/crates/hir-def/src/expr_store/body.rs b/crates/hir-def/src/expr_store/body.rs index a55fec4f8b..3177b2c74a 100644 --- a/crates/hir-def/src/expr_store/body.rs +++ b/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/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index dfc716eb84..0d3a542a43 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/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/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 6a0b1e5197..6ba0bbd61c 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/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/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index c8efd90432..95700b54db 100644 --- a/crates/hir-def/src/lib.rs +++ b/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> { + 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; #[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 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/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index c31d322132..25391c910e 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/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/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 7e13ae2f7a..5299894296 100644 --- a/crates/hir-def/src/resolver.rs +++ b/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/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs index 28b6812139..f8fb700d55 100644 --- a/crates/hir-expand/src/builtin/derive_macro.rs +++ b/crates/hir-expand/src/builtin/derive_macro.rs @@ -80,9 +80,15 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option BuiltinDeriveExpander::find_by_name(ident) } +#[derive(Clone, Copy)] +enum HasDefault { + Yes, + No, +} + #[derive(Clone)] enum VariantShape { - Struct(Vec), + Struct(Vec<(tt::Ident, HasDefault)>), Tuple(usize), Unit, } @@ -98,7 +104,7 @@ impl VariantShape { fn field_names(&self, span: Span) -> Vec { 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::>()?, ), 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/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index 99ec1951a3..03218b4691 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/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/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 3c258e3c4c..1b2ef2aef3 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -134,6 +134,9 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { + 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/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index cc6ed122af..23072011a7 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -2130,6 +2130,10 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result 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/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index 2a26101ac4..193b7bcd97 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/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/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 69ec35f406..a5af712b42 100644 --- a/crates/hir-ty/src/tests.rs +++ b/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/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index 537401afdc..dd26e894d7 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs @@ -147,6 +147,7 @@ impl From 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 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/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 27723cbc16..8a3aa14047 100644 --- a/crates/hir/src/lib.rs +++ b/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> { + 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/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index bad5360805..6925880ba9 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -972,6 +972,7 @@ impl TryFrom 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/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 7963e8ae4f..d2a237a5c0 100644 --- a/crates/ide-db/src/search.rs +++ b/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/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 56afb38cc8..7bc1be3822 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1232,6 +1232,21 @@ fn f() { let (_, _, _, ..) = (true, 42); // ^^^^^^^^^^^^^ error: expected (bool, i32), found (bool, i32, {unknown}) } +"#, + ); + } + + #[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/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 389c01933c..fe1316c9bf 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/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/crates/parser/src/grammar/items/adt.rs b/crates/parser/src/grammar/items/adt.rs index 21078175c0..9a16c9db6d 100644 --- a/crates/parser/src/grammar/items/adt.rs +++ b/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/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index b9f87b6af2..c8ea8c547a 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/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/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast b/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast new file mode 100644 index 0000000000..feb617e1aa --- /dev/null +++ b/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/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs b/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs new file mode 100644 index 0000000000..f1ecdf89fa --- /dev/null +++ b/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/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast b/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast index 08ae906421..12b4e233e3 100644 --- a/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast +++ b/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/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs b/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs index 65398ccb88..416cd763fd 100644 --- a/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs +++ b/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/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast b/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast new file mode 100644 index 0000000000..33088f2cab --- /dev/null +++ b/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/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs b/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs new file mode 100644 index 0000000000..d7b38944a8 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs @@ -0,0 +1 @@ +struct S { f: f32 = 0.0 } diff --git a/crates/parser/test_data/parser/inline/ok/record_lit.rast b/crates/parser/test_data/parser/inline/ok/record_lit.rast index 00948c322f..b868da55bc 100644 --- a/crates/parser/test_data/parser/inline/ok/record_lit.rast +++ b/crates/parser/test_data/parser/inline/ok/record_lit.rast @@ -120,6 +120,53 @@ SOURCE_FILE 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 + 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 diff --git a/crates/parser/test_data/parser/inline/ok/record_lit.rs b/crates/parser/test_data/parser/inline/ok/record_lit.rs index 86411fbb7d..42895f759b 100644 --- a/crates/parser/test_data/parser/inline/ok/record_lit.rs +++ b/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/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast b/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast new file mode 100644 index 0000000000..987e219ae8 --- /dev/null +++ b/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/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs b/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs new file mode 100644 index 0000000000..e08204f94c --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs @@ -0,0 +1,3 @@ +fn foo() { + let _s = S { .. }; +} diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index cd709afe09..824f262ca3 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/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/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 4e2a70d6cd..bbb8413cbc 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -241,7 +241,7 @@ RecordFieldList = RecordField = Attr* Visibility? - Name ':' Type + Name ':' Type ('=' Expr)? TupleFieldList = '(' fields:(TupleField (',' TupleField)* ','?)? ')' diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 69e2a9f9c1..8f10ea9464 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -1538,10 +1538,14 @@ impl ast::HasDocComments for RecordField {} impl ast::HasName for RecordField {} impl ast::HasVisibility for RecordField {} impl RecordField { + #[inline] + pub fn expr(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn ty(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn colon_token(&self) -> Option { support::token(&self.syntax, T![:]) } + #[inline] + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] From e6a103ae50699db1dbb0676d075a4bcda2247939 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 24 Jan 2025 02:54:54 +0900 Subject: [PATCH 2/8] Handle missing fields diagnostics --- crates/hir-def/src/data/adt.rs | 2 ++ crates/hir-def/src/expr_store/lower.rs | 5 +-- crates/hir-def/src/expr_store/pretty.rs | 2 +- crates/hir-def/src/hir.rs | 1 + crates/hir-def/src/item_tree.rs | 1 + crates/hir-def/src/item_tree/lower.rs | 5 +-- crates/hir-def/src/item_tree/pretty.rs | 8 +++-- crates/hir-ty/src/diagnostics/expr.rs | 12 +++++-- crates/hir-ty/src/infer/mutability.rs | 2 +- crates/hir-ty/src/mir/lower.rs | 2 +- .../src/handlers/missing_fields.rs | 31 +++++++++++++++++++ 11 files changed, 59 insertions(+), 12 deletions(-) diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 8fc1985403..aaa260a358 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/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/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 0d3a542a43..65580bce4d 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/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/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 6ba0bbd61c..9a8a8c2cd0 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/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/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 0dcddf162b..1e2417ecdf 100644 --- a/crates/hir-def/src/hir.rs +++ b/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/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 79ee3344d2..09fb5f6fd2 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/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/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 71848845a8..69a1933079 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/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/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index 70bf2f13c8..1e765ac78e 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/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/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 0b5f131924..d8700e2777 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/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/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index d74a383f44..5b6c3cd152 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/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/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 23072011a7..85e8d17203 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/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/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 938b7182bc..c495df9daa 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/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 + .. + }; +} +"#, + ); + } } From c134b20c9cbc88a36e77acb8522e8dc4573bd906 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 26 Jan 2025 17:23:23 +0900 Subject: [PATCH 3/8] Merge record lit's ellipsis into pre-existing spread's variant --- crates/hir-def/src/expr_store.rs | 6 +++--- crates/hir-def/src/expr_store/lower.rs | 12 +++++++----- crates/hir-def/src/expr_store/pretty.rs | 18 ++++++++++++------ crates/hir-def/src/hir.rs | 10 ++++++++-- crates/hir-ty/src/diagnostics/expr.rs | 13 ++++++++----- crates/hir-ty/src/infer/closure.rs | 4 ++-- crates/hir-ty/src/infer/expr.rs | 4 ++-- crates/hir-ty/src/infer/mutability.rs | 12 ++++++++---- crates/hir-ty/src/mir/lower.rs | 8 ++++---- crates/hir/src/source_analyzer.rs | 2 +- 10 files changed, 55 insertions(+), 34 deletions(-) diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index 9df6eaade7..d0695d0eac 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs @@ -25,7 +25,7 @@ use crate::{ db::DefDatabase, hir::{ Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, - PatId, RecordFieldPat, Statement, + PatId, RecordFieldPat, Spread, Statement, }, nameres::DefMap, path::{ModPath, Path}, @@ -362,7 +362,7 @@ impl ExpressionStore { for field in fields.iter() { f(field.expr); } - if let &Some(expr) = spread { + if let &Spread::Base(expr) = spread { f(expr); } } @@ -490,7 +490,7 @@ impl ExpressionStore { for field in fields.iter() { f(field.expr); } - if let &Some(expr) = spread { + if let &Spread::Base(expr) = spread { f(expr); } } diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 65580bce4d..3ce00b78a9 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -45,7 +45,7 @@ use crate::{ }, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, - OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, + OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Spread, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -602,11 +602,13 @@ impl ExprCollector<'_> { Some(RecordLitField { name, expr }) }) .collect(); - let spread = nfl.spread().map(|s| self.collect_expr(s)); - let ellipsis = nfl.dotdot_token().is_some(); - Expr::RecordLit { path, fields, spread, ellipsis } + let spread = nfl.spread().map(|s| self.collect_expr(s)).map_or_else( + || if nfl.dotdot_token().is_some() { Spread::Yes } else { Spread::No }, + Spread::Base, + ); + Expr::RecordLit { path, fields, spread } } else { - Expr::RecordLit { path, fields: Box::default(), spread: None, ellipsis: false } + Expr::RecordLit { path, fields: Box::default(), spread: Spread::No } }; self.alloc_expr(record_lit, syntax_ptr) diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 9a8a8c2cd0..1b3a1bb4dc 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs @@ -8,7 +8,7 @@ use span::Edition; use crate::{ hir::{ Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, LiteralOrConst, Movability, - Statement, + Spread, Statement, }, pretty::{print_generic_args, print_path, print_type_ref}, VariantId, @@ -398,7 +398,7 @@ impl Printer<'_> { self.print_expr(*expr); } } - Expr::RecordLit { path, fields, spread, ellipsis: _ } => { + Expr::RecordLit { path, fields, spread } => { match path { Some(path) => self.print_path(path), None => w!(self, "�"), @@ -412,10 +412,16 @@ impl Printer<'_> { p.print_expr(field.expr); wln!(p, ","); } - if let Some(spread) = spread { - w!(p, ".."); - p.print_expr(*spread); - wln!(p); + match spread { + Spread::No => {} + Spread::Yes => { + w!(p, ".."); + } + Spread::Base(expr) => { + w!(p, ".."); + p.print_expr(*expr); + wln!(p); + } } }); w!(self, "}}"); diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 1e2417ecdf..e09ce67a89 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -251,8 +251,7 @@ pub enum Expr { RecordLit { path: Option>, fields: Box<[RecordLitField]>, - spread: Option, - ellipsis: bool, + spread: Spread, }, Field { expr: ExprId, @@ -479,6 +478,13 @@ pub struct RecordLitField { pub expr: ExprId, } +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Spread { + No, + Yes, + Base(ExprId), +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum Statement { Let { diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index d8700e2777..dd55febbf0 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -8,6 +8,7 @@ use base_db::CrateId; use chalk_solve::rust_ir::AdtKind; use either::Either; use hir_def::{ + hir::Spread, lang_item::LangItem, resolver::{HasResolver, ValueNs}, AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup, @@ -546,9 +547,11 @@ pub fn record_literal_missing_fields( infer: &InferenceResult, id: ExprId, expr: &Expr, -) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> { - let (fields, exhaustive, ellipsis) = match expr { - Expr::RecordLit { fields, spread, ellipsis, .. } => (fields, spread.is_none(), *ellipsis), +) -> Option<(VariantId, Vec, /*has spread expr*/ bool)> { + let (fields, has_spread_expr, has_ellipsis) = match expr { + Expr::RecordLit { fields, spread, .. } => { + (fields, matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes)) + } _ => return None, }; @@ -564,7 +567,7 @@ pub fn record_literal_missing_fields( .fields() .iter() .filter_map(|(f, d)| { - if (ellipsis && d.has_default) || specified_fields.contains(&d.name) { + if (has_ellipsis && d.has_default) || specified_fields.contains(&d.name) { None } else { Some(f) @@ -574,7 +577,7 @@ pub fn record_literal_missing_fields( if missed_fields.is_empty() { return None; } - Some((variant_def, missed_fields, exhaustive)) + Some((variant_def, missed_fields, has_spread_expr)) } pub fn record_pattern_missing_fields( diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 9283c46d0f..0e9aed4160 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -12,7 +12,7 @@ use hir_def::{ data::adt::VariantData, hir::{ Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId, - Statement, UnaryOp, + Spread, Statement, UnaryOp, }, lang_item::LangItem, path::Path, @@ -796,7 +796,7 @@ impl InferenceContext<'_> { self.consume_expr(expr); } Expr::RecordLit { fields, spread, .. } => { - if let &Some(expr) = spread { + if let &Spread::Base(expr) = spread { self.consume_expr(expr); } self.consume_exprs(fields.iter().map(|it| it.expr)); diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index b951443897..8b3ec1ff2a 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -10,7 +10,7 @@ use either::Either; use hir_def::{ hir::{ ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId, - LabelId, Literal, Pat, PatId, Statement, UnaryOp, + LabelId, Literal, Pat, PatId, Spread, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs, Path}, @@ -775,7 +775,7 @@ impl InferenceContext<'_> { } } } - if let Some(expr) = spread { + if let Spread::Base(expr) = spread { self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes); } ty diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 5b6c3cd152..e95a425498 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -4,8 +4,8 @@ use chalk_ir::{cast::Cast, Mutability}; use hir_def::{ hir::{ - Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, - UnaryOp, + Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Spread, + Statement, UnaryOp, }, lang_item::LangItem, }; @@ -121,8 +121,12 @@ impl InferenceContext<'_> { Expr::Become { expr } => { self.infer_mut_expr(*expr, Mutability::Not); } - Expr::RecordLit { path: _, fields, spread, ellipsis: _ } => { - self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) + Expr::RecordLit { path: _, fields, spread } => { + let spread_expr = match spread { + Spread::Base(expr) => Some(*expr), + _ => None, + }; + self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(spread_expr)) } &Expr::Index { base, index } => { if mutability == Mutability::Mut { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 85e8d17203..5d89ebd4ef 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -9,7 +9,7 @@ use hir_def::{ expr_store::{Body, HygieneId}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, - LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, + LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Spread, }, lang_item::{LangItem, LangItemTarget}, path::Path, @@ -823,16 +823,16 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Become { .. } => not_supported!("tail-calls"), Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, spread, ellipsis: _ } => { + Expr::RecordLit { fields, path, spread } => { let spread_place = match spread { - &Some(it) => { + &Spread::Base(it) => { let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { return Ok(None); }; current = c; Some(p) } - None => None, + _ => None, }; let variant_id = self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 496b6566bd..653c858e58 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -1023,7 +1023,7 @@ impl SourceAnalyzer { let expr_id = self.expr_id(db, &literal.clone().into())?; let substs = infer[expr_id].as_adt()?.1; - let (variant, missing_fields, _exhaustive) = match expr_id { + let (variant, missing_fields, _) = match expr_id { ExprOrPatId::ExprId(expr_id) => { record_literal_missing_fields(db, infer, expr_id, &body[expr_id])? } From b4d4d02db8a95f5507fbd0aa90904d7b774f0027 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 26 Jan 2025 23:33:33 +0900 Subject: [PATCH 4/8] Compute diagnostics of a field body iff it has one --- crates/hir-def/src/lib.rs | 1 + crates/hir-ty/src/infer/closure.rs | 5 ++++- crates/hir-ty/src/infer/expr.rs | 12 ++++++++---- crates/hir-ty/src/mir/lower.rs | 11 ++++++++--- .../hir-ty/src/mir/lower/pattern_matching.rs | 10 ++++++++-- crates/hir/src/display.rs | 4 ++-- crates/hir/src/from_id.rs | 4 ++-- crates/hir/src/lib.rs | 15 +++++++++++---- crates/hir/src/semantics/child_by_source.rs | 6 +++++- crates/hir/src/source_analyzer.rs | 18 +++++++++++++++--- 10 files changed, 64 insertions(+), 22 deletions(-) diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 95700b54db..1f06331783 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -519,6 +519,7 @@ pub type LocalModuleId = Idx; pub struct FieldId { pub parent: VariantId, pub local_id: LocalFieldId, + pub has_default: bool, } impl FieldId { diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 0e9aed4160..def0cb705d 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -1167,9 +1167,11 @@ impl InferenceContext<'_> { }; let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); + let has_default = vd.fields()[local_id].has_default; p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id, + has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); @@ -1219,12 +1221,13 @@ impl InferenceContext<'_> { .iter() .zip(fields.clone()) .chain(ar.iter().rev().zip(fields.rev())); - for (&arg, (i, _)) in it { + for (&arg, (i, d)) in it { let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id: i, + has_default: d.has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 8b3ec1ff2a..b91f32b09e 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -1746,13 +1746,17 @@ impl InferenceContext<'_> { }); } TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { - let local_id = self.db.struct_data(*s).variant_data.field(name)?; - let field = FieldId { parent: (*s).into(), local_id }; + let vd = &self.db.struct_data(*s).variant_data; + let local_id = vd.field(name)?; + let has_default = vd.fields()[local_id].has_default; + let field = FieldId { parent: (*s).into(), local_id, has_default }; (field, parameters.clone()) } TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { - let local_id = self.db.union_data(*u).variant_data.field(name)?; - let field = FieldId { parent: (*u).into(), local_id }; + let vd = &self.db.union_data(*u).variant_data; + let local_id = vd.field(name)?; + let has_default = vd.fields()[local_id].has_default; + let field = FieldId { parent: (*u).into(), local_id, has_default }; (field, parameters.clone()) } _ => return None, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 5d89ebd4ef..b254bc8e54 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -870,12 +870,15 @@ impl<'ctx> MirLowerCtx<'ctx> { .map(|(i, it)| match it { Some(it) => it, None => { + let local_id = + LocalFieldId::from_raw(RawIdx::from(i as u32)); + let has_default = + variant_data.fields()[local_id].has_default; let p = sp.project( ProjectionElem::Field(Either::Left(FieldId { parent: variant_id, - local_id: LocalFieldId::from_raw(RawIdx::from( - i as u32, - )), + local_id, + has_default, })), &mut self.result.projection_store, ); @@ -897,10 +900,12 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; + let has_default = variant_data.fields()[local_id].has_default; let place = place.project( PlaceElem::Field(Either::Left(FieldId { parent: union_id.into(), local_id, + has_default, })), &mut self.result.projection_store, ); diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 2ffea34c85..43e6eb8898 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -632,10 +632,12 @@ impl MirLowerCtx<'_> { .map(|x| { let field_id = variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; + let has_default = variant_data.fields()[field_id].has_default; Ok(( PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: field_id, + has_default, })), x.pat, )) @@ -644,8 +646,12 @@ impl MirLowerCtx<'_> { self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? } AdtPatternShape::Tuple { args, ellipsis } => { - let fields = variant_data.fields().iter().map(|(x, _)| { - PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: x })) + let fields = variant_data.fields().iter().map(|(x, d)| { + PlaceElem::Field(Either::Left(FieldId { + parent: v, + local_id: x, + has_default: d.has_default, + })) }); self.pattern_match_tuple_like( current, diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 6f40497055..5591af9202 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -252,8 +252,8 @@ impl HirDisplay for Struct { f.write_char('(')?; let mut it = variant_data.fields().iter().peekable(); - while let Some((id, _)) = it.next() { - let field = Field { parent: (*self).into(), id }; + while let Some((id, d)) = it.next() { + let field = Field { parent: (*self).into(), id, has_default: d.has_default }; write_visibility(module_id, field.visibility(f.db), f)?; field.ty(f.db).hir_fmt(f)?; if it.peek().is_some() { diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index dd26e894d7..5c12cf21da 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs @@ -237,13 +237,13 @@ impl From for VariantId { impl From for FieldId { fn from(def: Field) -> Self { - FieldId { parent: def.parent.into(), local_id: def.id } + FieldId { parent: def.parent.into(), local_id: def.id, has_default: def.has_default } } } impl From for Field { fn from(def: FieldId) -> Self { - Field { parent: def.parent.into(), id: def.local_id } + Field { parent: def.parent.into(), id: def.local_id, has_default: def.has_default } } } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8a3aa14047..4d8b1f58f6 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -423,6 +423,9 @@ impl ModuleDef { }; if let Some(fields) = fields { for field in fields { + if !field.has_default { + continue; + } let def: DefWithBody = field.into(); def.diagnostics(db, &mut acc, style_lints); } @@ -1249,6 +1252,7 @@ impl From<&Field> for DefWithBodyId { pub struct Field { pub(crate) parent: VariantDef, pub(crate) id: LocalFieldId, + pub(crate) has_default: bool, } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -1414,7 +1418,7 @@ impl Struct { .variant_data .fields() .iter() - .map(|(id, _)| Field { parent: self.into(), id }) + .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) .collect() } @@ -1476,7 +1480,7 @@ impl Union { .variant_data .fields() .iter() - .map(|(id, _)| Field { parent: self.into(), id }) + .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) .collect() } @@ -1606,7 +1610,7 @@ impl Variant { self.variant_data(db) .fields() .iter() - .map(|(id, _)| Field { parent: self.into(), id }) + .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) .collect() } @@ -5162,10 +5166,13 @@ impl Type { _ => return Vec::new(), }; + let var_data = db.variant_data(variant_id); + let fields = var_data.fields(); db.field_types(variant_id) .iter() .map(|(local_id, ty)| { - let def = Field { parent: variant_id.into(), id: local_id }; + let has_default = fields[local_id].has_default; + let def = Field { parent: variant_id.into(), id: local_id, has_default }; let ty = ty.clone().substitute(Interner, substs); (def, self.derived(ty)) }) diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs index d5dfb98571..ef10a4e148 100644 --- a/crates/hir/src/semantics/child_by_source.rs +++ b/crates/hir/src/semantics/child_by_source.rs @@ -160,7 +160,11 @@ impl ChildBySource for VariantId { let arena_map = arena_map.as_ref(); let parent = *self; for (local_id, source) in arena_map.value.iter() { - let id = FieldId { parent, local_id }; + let has_default = match source { + Either::Left(_) => false, + Either::Right(rec) => rec.expr().is_some(), + }; + let id = FieldId { parent, local_id, has_default }; match source.clone() { Either::Left(source) => res[keys::TUPLE_FIELD].insert(AstPtr::new(&source), id), Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id), diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 653c858e58..65cf572dea 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -630,7 +630,10 @@ impl SourceAnalyzer { let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.variant_data(db.upcast()); - let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; + let fields = variant_data.fields(); + let local_id = variant_data.field(&local_name)?; + let field = + FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); Some(( @@ -651,7 +654,10 @@ impl SourceAnalyzer { let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); - let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; + let fields = variant_data.fields(); + let local_id = variant_data.field(&field_name)?; + let field = + FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); @@ -1060,11 +1066,17 @@ impl SourceAnalyzer { missing_fields: Vec, ) -> Vec<(Field, Type)> { let field_types = db.field_types(variant); + let var_data = db.variant_data(variant); + let fields = var_data.fields(); missing_fields .into_iter() .map(|local_id| { - let field = FieldId { parent: variant, local_id }; + let field = FieldId { + parent: variant, + local_id, + has_default: fields[local_id].has_default, + }; let ty = field_types[local_id].clone().substitute(Interner, substs); (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) }) From ce9da9063097c26006886b3f403a0c50790c285a Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 27 Jan 2025 00:47:31 +0900 Subject: [PATCH 5/8] Implement `HasResolver` and `HasModule` for `FieldId` --- crates/hir-def/src/lib.rs | 12 +++++++----- crates/hir-def/src/resolver.rs | 27 +++++++++++++-------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 1f06331783..cf97d27fd1 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -1357,6 +1357,12 @@ impl HasModule for VariantId { } } +impl HasModule for FieldId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { + self.parent.module(db) + } +} + impl HasModule for MacroId { fn module(&self, db: &dyn DefDatabase) -> ModuleId { match *self { @@ -1380,11 +1386,7 @@ 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), - }, + TypeOwnerId::FieldId(it) => it.module(db), } } } diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 5299894296..b415efafa6 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -27,10 +27,11 @@ use crate::{ type_ref::{LifetimeRef, TypesMap}, visibility::{RawVisibility, Visibility}, AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, - ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule, - ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, - MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, - TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId, + ExternBlockId, ExternCrateId, FieldId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, + HasModule, ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, + Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, + TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, + VariantId, }; #[derive(Debug, Clone)] @@ -1227,11 +1228,7 @@ 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), - }, + TypeOwnerId::FieldId(it) => it.resolver(db), } } } @@ -1244,11 +1241,7 @@ 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), - }, + DefWithBodyId::FieldId(f) => f.resolver(db), } } } @@ -1295,6 +1288,12 @@ impl HasResolver for VariantId { } } +impl HasResolver for FieldId { + fn resolver(self, db: &dyn DefDatabase) -> Resolver { + self.parent.resolver(db) + } +} + impl HasResolver for MacroId { fn resolver(self, db: &dyn DefDatabase) -> Resolver { match self { From 4fe18a6fb5a1181a04c47391f558ebab5b8b0f39 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 27 Jan 2025 00:48:19 +0900 Subject: [PATCH 6/8] Add a test for field default value body as defining usage of TAIT --- crates/hir-def/src/lib.rs | 6 +- crates/hir-ty/src/tests.rs | 112 ++++++++++++++---- .../src/tests/type_alias_impl_traits.rs | 50 +++++++- 3 files changed, 141 insertions(+), 27 deletions(-) diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index cf97d27fd1..80aa551660 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -1399,11 +1399,7 @@ 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), - }, + DefWithBodyId::FieldId(it) => it.module(db), } } } diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index a5af712b42..5177ed07b1 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -16,6 +16,7 @@ use std::env; use std::sync::LazyLock; use base_db::SourceDatabaseFileInputExt as _; +use either::Either; use expect_test::Expect; use hir_def::{ db::DefDatabase, @@ -23,12 +24,14 @@ use hir_def::{ hir::{ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, - src::HasSource, - AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId, SyntheticSyntax, + src::{HasChildSource, HasSource}, + AdtId, AssocItemId, DefWithBodyId, FieldId, HasModule, LocalModuleId, Lookup, ModuleDefId, + SyntheticSyntax, }; use hir_expand::{db::ExpandDatabase, FileRange, InFile}; use itertools::Itertools; use rustc_hash::FxHashMap; +use span::TextSize; use stdx::format_to; use syntax::{ ast::{self, AstNode, HasName}, @@ -132,14 +135,40 @@ fn check_impl( None => continue, }; let def_map = module.def_map(&db); - visit_module(&db, &def_map, module.local_id, &mut |it| { - defs.push(match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, - }) + visit_module(&db, &def_map, module.local_id, &mut |it| match it { + ModuleDefId::FunctionId(it) => defs.push(it.into()), + ModuleDefId::EnumVariantId(it) => { + defs.push(it.into()); + let variant_id = it.into(); + let vd = db.variant_data(variant_id); + defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { + if fd.has_default { + let field = FieldId { parent: variant_id, local_id, has_default: true }; + Some(DefWithBodyId::FieldId(field)) + } else { + None + } + })); + } + ModuleDefId::ConstId(it) => defs.push(it.into()), + ModuleDefId::StaticId(it) => defs.push(it.into()), + ModuleDefId::AdtId(it) => { + let variant_id = match it { + AdtId::StructId(it) => it.into(), + AdtId::UnionId(it) => it.into(), + AdtId::EnumId(_) => return, + }; + let vd = db.variant_data(variant_id); + defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { + if fd.has_default { + let field = FieldId { parent: variant_id, local_id, has_default: true }; + Some(DefWithBodyId::FieldId(field)) + } else { + None + } + })); + } + _ => {} }); } defs.sort_by_key(|def| match def { @@ -160,12 +189,20 @@ fn check_impl( loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), - DefWithBodyId::FieldId(_) => unreachable!(), + DefWithBodyId::FieldId(it) => { + let cs = it.parent.child_source(&db); + match cs.value.get(it.local_id) { + Some(Either::Left(it)) => it.syntax().text_range().start(), + Some(Either::Right(it)) => it.syntax().text_range().end(), + None => TextSize::new(u32::MAX), + } + } }); let mut unexpected_type_mismatches = String::new(); for def in defs { let (body, body_source_map) = db.body_with_source_map(def); let inference_result = db.infer(def); + dbg!(&inference_result); for (pat, mut ty) in inference_result.type_of_pat.iter() { if let Pat::Bind { id, .. } = body.pats[pat] { @@ -389,14 +426,40 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let def_map = module.def_map(&db); let mut defs: Vec = Vec::new(); - visit_module(&db, &def_map, module.local_id, &mut |it| { - defs.push(match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, - }) + visit_module(&db, &def_map, module.local_id, &mut |it| match it { + ModuleDefId::FunctionId(it) => defs.push(it.into()), + ModuleDefId::EnumVariantId(it) => { + defs.push(it.into()); + let variant_id = it.into(); + let vd = db.variant_data(variant_id); + defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { + if fd.has_default { + let field = FieldId { parent: variant_id, local_id, has_default: true }; + Some(DefWithBodyId::FieldId(field)) + } else { + None + } + })); + } + ModuleDefId::ConstId(it) => defs.push(it.into()), + ModuleDefId::StaticId(it) => defs.push(it.into()), + ModuleDefId::AdtId(it) => { + let variant_id = match it { + AdtId::StructId(it) => it.into(), + AdtId::UnionId(it) => it.into(), + AdtId::EnumId(_) => return, + }; + let vd = db.variant_data(variant_id); + defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { + if fd.has_default { + let field = FieldId { parent: variant_id, local_id, has_default: true }; + Some(DefWithBodyId::FieldId(field)) + } else { + None + } + })); + } + _ => {} }); defs.sort_by_key(|def| match def { DefWithBodyId::FunctionId(it) => { @@ -416,7 +479,14 @@ 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!(), + DefWithBodyId::FieldId(it) => { + let cs = it.parent.child_source(&db); + match cs.value.get(it.local_id) { + Some(Either::Left(it)) => it.syntax().text_range().start(), + Some(Either::Right(it)) => it.syntax().text_range().end(), + None => TextSize::new(u32::MAX), + } + } }); for def in defs { let (body, source_map) = db.body_with_source_map(def); @@ -477,7 +547,7 @@ pub(crate) fn visit_module( let body = db.body(it.into()); visit_body(db, &body, cb); } - ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => { + ModuleDefId::AdtId(AdtId::EnumId(it)) => { db.enum_data(it).variants.iter().for_each(|&(it, _)| { let body = db.body(it.into()); cb(it.into()); diff --git a/crates/hir-ty/src/tests/type_alias_impl_traits.rs b/crates/hir-ty/src/tests/type_alias_impl_traits.rs index e2b7bf379c..a2aada57da 100644 --- a/crates/hir-ty/src/tests/type_alias_impl_traits.rs +++ b/crates/hir-ty/src/tests/type_alias_impl_traits.rs @@ -157,5 +157,53 @@ static ALIAS: i32 = { 217..218 '5': i32 205..211: expected impl Trait + ?Sized, got Struct "#]], - ) + ); +} + +#[test] +fn defining_type_alias_impl_trait_from_default_fields() { + check_no_mismatches( + r#" +trait Trait {} + +struct Struct; + +impl Trait for Struct {} + +type AliasTy = impl Trait; + +struct Foo { + foo: AliasTy = { + let x: AliasTy = Struct; + x + }, +} +"#, + ); + + check_infer_with_mismatches( + r#" +trait Trait {} + +struct Struct; + +impl Trait for Struct {} + +type AliasTy = impl Trait; + +struct Foo { + foo: i32 = { + let x: AliasTy = Struct; + 5 + }, +} +"#, + expect![[r#" + 114..164 '{ ... }': i32 + 128..129 'x': impl Trait + ?Sized + 141..147 'Struct': Struct + 157..158 '5': i32 + 141..147: expected impl Trait + ?Sized, got Struct + "#]], + ); } From e5c38558f5dbc37cbc91f9fda58144ce02e1f5aa Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 27 Jan 2025 00:58:26 +0900 Subject: [PATCH 7/8] Fix a mistake in condition --- crates/hir-ty/src/diagnostics/expr.rs | 2 +- crates/hir-ty/src/tests.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index dd55febbf0..d744fe64c0 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -550,7 +550,7 @@ pub fn record_literal_missing_fields( ) -> Option<(VariantId, Vec, /*has spread expr*/ bool)> { let (fields, has_spread_expr, has_ellipsis) = match expr { Expr::RecordLit { fields, spread, .. } => { - (fields, matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes)) + (fields, !matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes)) } _ => return None, }; diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 5177ed07b1..56b7e6cba0 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -202,7 +202,6 @@ fn check_impl( for def in defs { let (body, body_source_map) = db.body_with_source_map(def); let inference_result = db.infer(def); - dbg!(&inference_result); for (pat, mut ty) in inference_result.type_of_pat.iter() { if let Pat::Bind { id, .. } = body.pats[pat] { From 8aa6c09fcee6270c787a6f00615d76343fbe5c07 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 27 Jan 2025 19:20:26 +0900 Subject: [PATCH 8/8] Remove `has_default` from `FieldId` --- crates/hir-def/src/lib.rs | 1 - crates/hir-ty/src/infer/closure.rs | 5 +-- crates/hir-ty/src/infer/expr.rs | 6 ++-- crates/hir-ty/src/mir/lower.rs | 5 --- .../hir-ty/src/mir/lower/pattern_matching.rs | 10 ++---- crates/hir-ty/src/tests.rs | 8 ++--- crates/hir/src/display.rs | 4 +-- crates/hir/src/from_id.rs | 4 +-- crates/hir/src/lib.rs | 34 ++++++++++--------- crates/hir/src/semantics/child_by_source.rs | 6 +--- crates/hir/src/source_analyzer.rs | 16 ++------- 11 files changed, 35 insertions(+), 64 deletions(-) diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 80aa551660..6bc8741c33 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -519,7 +519,6 @@ pub type LocalModuleId = Idx; pub struct FieldId { pub parent: VariantId, pub local_id: LocalFieldId, - pub has_default: bool, } impl FieldId { diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index def0cb705d..0e9aed4160 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -1167,11 +1167,9 @@ impl InferenceContext<'_> { }; let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); - let has_default = vd.fields()[local_id].has_default; p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id, - has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); @@ -1221,13 +1219,12 @@ impl InferenceContext<'_> { .iter() .zip(fields.clone()) .chain(ar.iter().rev().zip(fields.rev())); - for (&arg, (i, d)) in it { + for (&arg, (i, _)) in it { let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id: i, - has_default: d.has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index b91f32b09e..179565440c 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -1748,15 +1748,13 @@ impl InferenceContext<'_> { TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { let vd = &self.db.struct_data(*s).variant_data; let local_id = vd.field(name)?; - let has_default = vd.fields()[local_id].has_default; - let field = FieldId { parent: (*s).into(), local_id, has_default }; + let field = FieldId { parent: (*s).into(), local_id }; (field, parameters.clone()) } TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { let vd = &self.db.union_data(*u).variant_data; let local_id = vd.field(name)?; - let has_default = vd.fields()[local_id].has_default; - let field = FieldId { parent: (*u).into(), local_id, has_default }; + let field = FieldId { parent: (*u).into(), local_id }; (field, parameters.clone()) } _ => return None, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index b254bc8e54..940d992001 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -872,13 +872,10 @@ impl<'ctx> MirLowerCtx<'ctx> { None => { let local_id = LocalFieldId::from_raw(RawIdx::from(i as u32)); - let has_default = - variant_data.fields()[local_id].has_default; let p = sp.project( ProjectionElem::Field(Either::Left(FieldId { parent: variant_id, local_id, - has_default, })), &mut self.result.projection_store, ); @@ -900,12 +897,10 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let has_default = variant_data.fields()[local_id].has_default; let place = place.project( PlaceElem::Field(Either::Left(FieldId { parent: union_id.into(), local_id, - has_default, })), &mut self.result.projection_store, ); diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 43e6eb8898..2ffea34c85 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -632,12 +632,10 @@ impl MirLowerCtx<'_> { .map(|x| { let field_id = variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; - let has_default = variant_data.fields()[field_id].has_default; Ok(( PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: field_id, - has_default, })), x.pat, )) @@ -646,12 +644,8 @@ impl MirLowerCtx<'_> { self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? } AdtPatternShape::Tuple { args, ellipsis } => { - let fields = variant_data.fields().iter().map(|(x, d)| { - PlaceElem::Field(Either::Left(FieldId { - parent: v, - local_id: x, - has_default: d.has_default, - })) + let fields = variant_data.fields().iter().map(|(x, _)| { + PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: x })) }); self.pattern_match_tuple_like( current, diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 56b7e6cba0..96e7130ecf 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -143,7 +143,7 @@ fn check_impl( let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; + let field = FieldId { parent: variant_id, local_id }; Some(DefWithBodyId::FieldId(field)) } else { None @@ -161,7 +161,7 @@ fn check_impl( let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; + let field = FieldId { parent: variant_id, local_id }; Some(DefWithBodyId::FieldId(field)) } else { None @@ -433,7 +433,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; + let field = FieldId { parent: variant_id, local_id }; Some(DefWithBodyId::FieldId(field)) } else { None @@ -451,7 +451,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; + let field = FieldId { parent: variant_id, local_id }; Some(DefWithBodyId::FieldId(field)) } else { None diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 5591af9202..6f40497055 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -252,8 +252,8 @@ impl HirDisplay for Struct { f.write_char('(')?; let mut it = variant_data.fields().iter().peekable(); - while let Some((id, d)) = it.next() { - let field = Field { parent: (*self).into(), id, has_default: d.has_default }; + while let Some((id, _)) = it.next() { + let field = Field { parent: (*self).into(), id }; write_visibility(module_id, field.visibility(f.db), f)?; field.ty(f.db).hir_fmt(f)?; if it.peek().is_some() { diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index 5c12cf21da..dd26e894d7 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs @@ -237,13 +237,13 @@ impl From for VariantId { impl From for FieldId { fn from(def: Field) -> Self { - FieldId { parent: def.parent.into(), local_id: def.id, has_default: def.has_default } + FieldId { parent: def.parent.into(), local_id: def.id } } } impl From for Field { fn from(def: FieldId) -> Self { - Field { parent: def.parent.into(), id: def.local_id, has_default: def.has_default } + Field { parent: def.parent.into(), id: def.local_id } } } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 4d8b1f58f6..5b35b0168a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -415,18 +415,24 @@ 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)), + let vd: Option<(VariantDef, Arc)> = match self { + ModuleDef::Adt(Adt::Struct(it)) => { + Some((it.into(), db.struct_data(it.id).variant_data.clone())) + } + ModuleDef::Adt(Adt::Union(it)) => { + Some((it.into(), db.union_data(it.id).variant_data.clone())) + } + ModuleDef::Variant(it) => { + Some((it.into(), db.enum_variant_data(it.id).variant_data.clone())) + } _ => None, }; - if let Some(fields) = fields { - for field in fields { - if !field.has_default { + if let Some((parent, vd)) = vd { + for (id, fd) in vd.fields().iter() { + if !fd.has_default { continue; } - let def: DefWithBody = field.into(); + let def: DefWithBody = DefWithBody::Field(Field { parent, id }); def.diagnostics(db, &mut acc, style_lints); } } @@ -1252,7 +1258,6 @@ impl From<&Field> for DefWithBodyId { pub struct Field { pub(crate) parent: VariantDef, pub(crate) id: LocalFieldId, - pub(crate) has_default: bool, } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -1418,7 +1423,7 @@ impl Struct { .variant_data .fields() .iter() - .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) + .map(|(id, _)| Field { parent: self.into(), id }) .collect() } @@ -1480,7 +1485,7 @@ impl Union { .variant_data .fields() .iter() - .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) + .map(|(id, _)| Field { parent: self.into(), id }) .collect() } @@ -1610,7 +1615,7 @@ impl Variant { self.variant_data(db) .fields() .iter() - .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) + .map(|(id, _)| Field { parent: self.into(), id }) .collect() } @@ -5166,13 +5171,10 @@ impl Type { _ => return Vec::new(), }; - let var_data = db.variant_data(variant_id); - let fields = var_data.fields(); db.field_types(variant_id) .iter() .map(|(local_id, ty)| { - let has_default = fields[local_id].has_default; - let def = Field { parent: variant_id.into(), id: local_id, has_default }; + let def = Field { parent: variant_id.into(), id: local_id }; let ty = ty.clone().substitute(Interner, substs); (def, self.derived(ty)) }) diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs index ef10a4e148..d5dfb98571 100644 --- a/crates/hir/src/semantics/child_by_source.rs +++ b/crates/hir/src/semantics/child_by_source.rs @@ -160,11 +160,7 @@ impl ChildBySource for VariantId { let arena_map = arena_map.as_ref(); let parent = *self; for (local_id, source) in arena_map.value.iter() { - let has_default = match source { - Either::Left(_) => false, - Either::Right(rec) => rec.expr().is_some(), - }; - let id = FieldId { parent, local_id, has_default }; + let id = FieldId { parent, local_id }; match source.clone() { Either::Left(source) => res[keys::TUPLE_FIELD].insert(AstPtr::new(&source), id), Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id), diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 65cf572dea..9f230c2251 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -630,10 +630,8 @@ impl SourceAnalyzer { let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.variant_data(db.upcast()); - let fields = variant_data.fields(); let local_id = variant_data.field(&local_name)?; - let field = - FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; + let field = FieldId { parent: variant, local_id }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); Some(( @@ -654,10 +652,8 @@ impl SourceAnalyzer { let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); - let fields = variant_data.fields(); let local_id = variant_data.field(&field_name)?; - let field = - FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; + let field = FieldId { parent: variant, local_id }; let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); @@ -1066,17 +1062,11 @@ impl SourceAnalyzer { missing_fields: Vec, ) -> Vec<(Field, Type)> { let field_types = db.field_types(variant); - let var_data = db.variant_data(variant); - let fields = var_data.fields(); missing_fields .into_iter() .map(|local_id| { - let field = FieldId { - parent: variant, - local_id, - has_default: fields[local_id].has_default, - }; + let field = FieldId { parent: variant, local_id }; let ty = field_types[local_id].clone().substitute(Interner, substs); (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) })