diff --git a/crates/hir-analysis/src/name_resolution/path_resolver.rs b/crates/hir-analysis/src/name_resolution/path_resolver.rs index 9adc6b3bb..c89105498 100644 --- a/crates/hir-analysis/src/name_resolution/path_resolver.rs +++ b/crates/hir-analysis/src/name_resolution/path_resolver.rs @@ -7,7 +7,10 @@ use super::{ }, NameDomain, NameQuery, }; -use crate::{name_resolution::QueryDirective, HirAnalysisDb}; +use crate::{ + name_resolution::{resolve_segments_early, QueryDirective}, + HirAnalysisDb, +}; /// The result of early path resolution. /// There are two kinds of early resolution results: diff --git a/crates/hir-analysis/src/ty/diagnostics.rs b/crates/hir-analysis/src/ty/diagnostics.rs index 3044ec8ee..b23cb2b51 100644 --- a/crates/hir-analysis/src/ty/diagnostics.rs +++ b/crates/hir-analysis/src/ty/diagnostics.rs @@ -16,7 +16,7 @@ use super::{ ty_check::ResolvedPathData, ty_def::{Kind, TyId}, }; -use crate::HirAnalysisDb; +use crate::{name_resolution::diagnostics::NameResDiag, HirAnalysisDb}; #[salsa::accumulator] pub struct AdtDefDiagAccumulator(pub(super) TyDiagCollection); @@ -37,6 +37,7 @@ pub struct FuncBodyDiagAccumulator(pub(super) FuncBodyDiag); pub enum FuncBodyDiag { Ty(TyDiagCollection), Body(BodyDiag), + NameRes(NameResDiag), } impl FuncBodyDiag { @@ -44,6 +45,7 @@ impl FuncBodyDiag { match self { Self::Ty(diag) => diag.to_voucher(), Self::Body(diag) => Box::new(diag.clone()) as _, + Self::NameRes(diag) => Box::new(diag.clone()) as _, } } } @@ -547,9 +549,19 @@ pub enum BodyDiag { }, UnitVariantExpectedInPat { primary: DynLazySpan, - kind: &'static str, + pat_kind: &'static str, hint: Option, }, + TupleVariantExpectedInPat { + primary: DynLazySpan, + pat_kind: Option<&'static str>, + hint: Option, + }, + MismatchedFieldCount { + primary: DynLazySpan, + expected: usize, + given: usize, + }, } impl BodyDiag { @@ -569,11 +581,29 @@ impl BodyDiag { primary: DynLazySpan, data: ResolvedPathData, ) -> Self { - let kind = data.data_kind(db); + let pat_kind = data.data_kind(db); let hint = data.initializer_hint(db); Self::UnitVariantExpectedInPat { primary, - kind, + pat_kind, + hint, + } + } + + pub(super) fn tuple_variant_expected_in_pat( + db: &dyn HirAnalysisDb, + primary: DynLazySpan, + data: Option, + ) -> Self { + let (pat_kind, hint) = if let Some(data) = data { + (Some(data.data_kind(db)), data.initializer_hint(db)) + } else { + (None, None) + }; + + Self::TupleVariantExpectedInPat { + primary, + pat_kind, hint, } } @@ -585,6 +615,8 @@ impl BodyDiag { Self::DuplicatedRestPat(_) => 2, Self::InvalidPathDomainInPat { .. } => 3, Self::UnitVariantExpectedInPat { .. } => 4, + Self::TupleVariantExpectedInPat { .. } => 5, + Self::MismatchedFieldCount { .. } => 6, } } @@ -595,6 +627,8 @@ impl BodyDiag { Self::DuplicatedRestPat(_) => "duplicated `..` found".to_string(), Self::InvalidPathDomainInPat { .. } => "invalid item is given here".to_string(), Self::UnitVariantExpectedInPat { .. } => "unit variant is expected here".to_string(), + Self::TupleVariantExpectedInPat { .. } => "tuple variant is expected here".to_string(), + Self::MismatchedFieldCount { .. } => "field count mismatch".to_string(), } } @@ -636,22 +670,64 @@ impl BodyDiag { Self::UnitVariantExpectedInPat { primary, - kind, + pat_kind, hint, } => { - let mut diags = vec![SubDiagnostic::new( + let mut diag = vec![SubDiagnostic::new( LabelStyle::Primary, - format!("expected unit variant here, but found {}", kind,), + format!("expected unit variant here, but found {}", pat_kind,), primary.resolve(db), )]; if let Some(hint) = hint { - diags.push(SubDiagnostic::new( + diag.push(SubDiagnostic::new( LabelStyle::Secondary, format!("Use {} instead", hint), primary.resolve(db), )) } - diags + diag + } + + Self::TupleVariantExpectedInPat { + primary, + pat_kind, + hint, + } => { + let mut diag = if let Some(pat_kind) = pat_kind { + vec![SubDiagnostic::new( + LabelStyle::Primary, + format!("expected tuple variant here, but found {}", pat_kind,), + primary.resolve(db), + )] + } else { + vec![SubDiagnostic::new( + LabelStyle::Primary, + "expected tuple variant here".to_string(), + primary.resolve(db), + )] + }; + + if let Some(hint) = hint { + diag.push(SubDiagnostic::new( + LabelStyle::Secondary, + format!("Use {} instead", hint), + primary.resolve(db), + )) + } + + diag + } + + Self::MismatchedFieldCount { + primary, + expected, + given, + } => { + vec![SubDiagnostic::new( + LabelStyle::Primary, + format!("expected {} fields here, but {} given", expected, given,), + primary.resolve(db), + )] } } } diff --git a/crates/hir-analysis/src/ty/ty_check/mod.rs b/crates/hir-analysis/src/ty/ty_check/mod.rs index 06c52a867..d2d8ef632 100644 --- a/crates/hir-analysis/src/ty/ty_check/mod.rs +++ b/crates/hir-analysis/src/ty/ty_check/mod.rs @@ -76,6 +76,10 @@ impl<'db> TyChecker<'db> { } } + fn body(&self) -> Body { + self.env.body() + } + fn lit_ty(&mut self, lit: &LitKind) -> TyId { match lit { LitKind::Bool(_) => TyId::bool(self.db), @@ -103,6 +107,10 @@ impl<'db> TyChecker<'db> { self.table.new_var(TyVarUniverse::General, &Kind::Star) } + fn fresh_tys_n(&mut self, n: usize) -> Vec { + (0..n).map(|_| self.fresh_ty()).collect() + } + fn unify_ty(&mut self, t: T, actual: TyId, expected: TyId) -> TyId where T: Into, @@ -274,4 +282,38 @@ impl ResolvedPathData { } } } + + fn is_tuple_variant(&self, db: &dyn HirAnalysisDb) -> bool { + match self { + Self::Adt(_, _) => false, + Self::Variant(enum_, idx, _) => { + let hir_db = db.as_hir_db(); + matches!( + enum_.variants(hir_db).data(hir_db)[*idx].kind, + HirVariantKind::Tuple(_) + ) + } + } + } + + fn field_tys(&self, db: &dyn HirAnalysisDb) -> Vec { + let (adt, idx) = match self { + Self::Adt(adt, _) => { + if matches!( + adt.adt_ref(db).data(db), + AdtRef::Struct(_) | AdtRef::Contract(_) + ) { + (*adt, 0) + } else { + return vec![]; + } + } + Self::Variant(enum_, idx, _) => { + let adt = lower_adt(db, AdtRefId::from_enum(db, *enum_)); + (adt, *idx) + } + }; + + adt.fields(db)[idx].iter_types(db).collect() + } } diff --git a/crates/hir-analysis/src/ty/ty_check/pat.rs b/crates/hir-analysis/src/ty/ty_check/pat.rs index f5ceb54cf..7e497e6a8 100644 --- a/crates/hir-analysis/src/ty/ty_check/pat.rs +++ b/crates/hir-analysis/src/ty/ty_check/pat.rs @@ -2,13 +2,14 @@ use std::ops::Range; use hir::{ hir_def::{scope_graph::ScopeId, Enum, IdentId, ItemKind, Partial, Pat, PatId, PathId}, - span::DynLazySpan, + span::path::LazyPathSpan, }; use super::{env::TyCheckEnv, ResolvedPathData, TyChecker}; use crate::{ name_resolution::{ - resolve_path_early, EarlyResolvedPath, NameDomain, NameRes, NameResBucket, NameResKind, + resolve_path_early, resolve_segments_early, EarlyResolvedPath, NameDomain, NameRes, + NameResBucket, NameResKind, }, ty::{ diagnostics::{BodyDiag, FuncBodyDiag, FuncBodyDiagAccumulator, TyLowerDiag}, @@ -20,152 +21,252 @@ use crate::{ impl<'db> TyChecker<'db> { pub(super) fn check_pat(&mut self, pat: PatId, expected: TyId) -> TyId { - let Partial::Present(pat_data) = pat.data(self.db.as_hir_db(), self.env.body()) else { + let Partial::Present(pat_data) = pat.data(self.db.as_hir_db(), self.body()) else { let actual = TyId::invalid(self.db, InvalidCause::Other); return self.unify_ty(pat, actual, expected); }; - match pat_data { + let ty = match pat_data { Pat::WildCard => { let ty_var = self.table.new_var(TyVarUniverse::General, &Kind::Star); self.unify_ty(pat, ty_var, expected) } Pat::Rest => unreachable!(), + Pat::Lit(..) => self.check_lit_pat(pat, pat_data), + Pat::Tuple(..) => self.check_tuple_pat(pat, pat_data, expected), + Pat::Path(..) => self.check_path_pat(pat, pat_data), + Pat::PathTuple(..) => self.check_path_tuple_pat(pat, pat_data), + Pat::Record(..) => todo!(), - Pat::Lit(lit) => { - let actual = match lit { - Partial::Present(lit) => self.lit_ty(lit), - Partial::Absent => TyId::invalid(self.db, InvalidCause::Other), - }; - self.unify_ty(pat, actual, expected) + Pat::Or(lhs, rhs) => { + self.check_pat(*lhs, expected); + self.check_pat(*rhs, expected) } + }; - Pat::Tuple(pat_tup) => { - let (actual, rest_range) = self.unpack_rest_pat(pat_tup, expected); - let unified = self.unify_ty(pat, actual, expected); + self.unify_ty(pat, ty, expected) + } - if unified.contains_invalid(self.db) { - pat_tup.iter().for_each(|&pat| { - self.env - .type_pat(pat, TyId::invalid(self.db, InvalidCause::Other)); - }); - return unified; - } + fn check_lit_pat(&mut self, _pat: PatId, pat_data: &Pat) -> TyId { + let Pat::Lit(lit) = pat_data else { + unreachable!() + }; - for (i, &pat_ty) in unified.decompose_ty_app(self.db).1.iter().enumerate() { - if rest_range.contains(&i) - || pat_tup[i].is_rest(self.db.as_hir_db(), self.env.body()) - { - continue; - } + match lit { + Partial::Present(lit) => self.lit_ty(lit), + Partial::Absent => TyId::invalid(self.db, InvalidCause::Other), + } + } + + fn check_tuple_pat(&mut self, pat: PatId, pat_data: &Pat, expected: TyId) -> TyId { + let Pat::Tuple(pat_tup) = pat_data else { + unreachable!() + }; + + let expected_len = match expected.decompose_ty_app(self.db) { + (base, args) if base.is_tuple(self.db) => Some(args.len()), + _ => None, + }; + let (actual, rest_range) = self.unpack_rest_pat(pat_tup, expected_len); + let actual = TyId::tuple_with_elems(self.db, &actual); + + let unified = self.unify_ty(pat, actual, expected); + if unified.contains_invalid(self.db) { + pat_tup.iter().for_each(|&pat| { + self.env + .type_pat(pat, TyId::invalid(self.db, InvalidCause::Other)); + }); + return unified; + } + + let mut pat_idx = 0; + for (i, &pat_ty) in unified.decompose_ty_app(self.db).1.iter().enumerate() { + if pat_idx >= pat_tup.len() { + break; + }; + + if pat_tup[pat_idx].is_rest(self.db.as_hir_db(), self.body()) { + pat_idx += 1; + continue; + } - self.check_pat(pat_tup[i], pat_ty); + if rest_range.contains(&i) { + continue; + } + + self.check_pat(pat_tup[pat_idx], pat_ty); + pat_idx += 1; + } + + unified + } + + fn check_path_pat(&mut self, pat: PatId, pat_data: &Pat) -> TyId { + let Pat::Path(path) = pat_data else { + unreachable!() + }; + + let Partial::Present(path) = path else { + return TyId::invalid(self.db, InvalidCause::Other); + }; + + match resolve_path_in_pat(self.db, &self.env, *path, pat) { + ResolvedPathInPat::Data(data) => { + if data.is_unit_variant(self.db) { + data.ty(self.db, &mut self.table) + } else { + let diag = BodyDiag::unit_variant_expected_in_pat( + self.db, + pat.lazy_span(self.body()).into(), + data, + ); + + FuncBodyDiagAccumulator::push(self.db, diag.into()); + TyId::invalid(self.db, InvalidCause::Other) } + } - unified + ResolvedPathInPat::NewBinding(binding) => { + self.env.register_pending_binding(binding, pat); + self.fresh_ty() } - Pat::Path(path) => { - let Partial::Present(path) = path else { - return TyId::invalid(self.db, InvalidCause::Other); - }; + ResolvedPathInPat::Diag(diag) => { + FuncBodyDiagAccumulator::push(self.db, diag); + TyId::invalid(self.db, InvalidCause::Other) + } - match resolve_path_in_pat(self.db, &self.env, *path, pat) { - ResolvedPathInPat::Data(data) => { - if data.is_unit_variant(self.db) { - let ty = data.ty(self.db, &mut self.table); - self.unify_ty(pat, ty, expected) - } else { - let diag = BodyDiag::unit_variant_expected_in_pat( - self.db, - pat.lazy_span(self.env.body()).into(), - data, - ); - - FuncBodyDiagAccumulator::push(self.db, diag.into()); - self.unify_ty( - pat, - TyId::invalid(self.db, InvalidCause::Other), - expected, - ) - } - } + ResolvedPathInPat::Invalid => TyId::invalid(self.db, InvalidCause::Other), + } + } - ResolvedPathInPat::NewBinding(binding) => { - self.env.register_pending_binding(binding, pat); - let actual = self.table.new_var(TyVarUniverse::General, &Kind::Star); - self.unify_ty(pat, actual, expected) - } + fn check_path_tuple_pat(&mut self, pat: PatId, pat_data: &Pat) -> TyId { + let Pat::PathTuple(path, args) = pat_data else { + unreachable!() + }; - ResolvedPathInPat::Diag(diag) => { - FuncBodyDiagAccumulator::push(self.db, diag); - TyId::invalid(self.db, InvalidCause::Other) - } + let Partial::Present(path) = path else { + return TyId::invalid(self.db, InvalidCause::Other); + }; + + let (pat_ty, expected_fields) = match resolve_path_in_pat(self.db, &self.env, *path, pat) { + ResolvedPathInPat::Data(data) => { + if data.is_tuple_variant(self.db) { + let ty = data.ty(self.db, &mut self.table); + let field_tys = data.field_tys(self.db); - ResolvedPathInPat::Invalid => TyId::invalid(self.db, InvalidCause::Other), + (ty, Some(field_tys)) + } else { + let diag = BodyDiag::tuple_variant_expected_in_pat( + self.db, + pat.lazy_span(self.body()).into(), + Some(data), + ); + FuncBodyDiagAccumulator::push(self.db, diag.into()); + + (TyId::invalid(self.db, InvalidCause::Other), None) } } - Pat::PathTuple(..) => todo!(), + ResolvedPathInPat::NewBinding(_) => { + let diag = BodyDiag::tuple_variant_expected_in_pat( + self.db, + pat.lazy_span(self.body()).into(), + None, + ); + FuncBodyDiagAccumulator::push(self.db, diag.into()); - Pat::Record(..) => todo!(), + (TyId::invalid(self.db, InvalidCause::Other), None) + } - Pat::Or(lhs, rhs) => { - self.check_pat(*lhs, expected); - self.check_pat(*rhs, expected) + ResolvedPathInPat::Diag(diag) => { + FuncBodyDiagAccumulator::push(self.db, diag); + + (TyId::invalid(self.db, InvalidCause::Other), None) + } + + ResolvedPathInPat::Invalid => (TyId::invalid(self.db, InvalidCause::Other), None), + }; + + let Some(expected_fields) = expected_fields else { + args.iter().for_each(|&pat| { + self.env + .type_pat(pat, TyId::invalid(self.db, InvalidCause::Other)); + }); + + return pat_ty; + }; + + let (actual_args, rest_range) = self.unpack_rest_pat(args, Some(expected_fields.len())); + if actual_args.len() != expected_fields.len() { + let diag = BodyDiag::MismatchedFieldCount { + primary: pat.lazy_span(self.body()).into(), + expected: expected_fields.len(), + given: actual_args.len(), + }; + + FuncBodyDiagAccumulator::push(self.db, diag.into()); + return pat_ty; + }; + + let mut arg_idx = 0; + for (i, &arg_ty) in expected_fields.iter().enumerate() { + if arg_idx >= args.len() { + break; + } + + if args[arg_idx].is_rest(self.db.as_hir_db(), self.body()) { + arg_idx += 1; + continue; + } + + if rest_range.contains(&i) { + continue; } + + self.check_pat(args[arg_idx], arg_ty); + arg_idx += 1; } + + pat_ty } fn unpack_rest_pat( &mut self, pat_tup: &[PatId], - expected: TyId, - ) -> (TyId, std::ops::Range) { + expected_len: Option, + ) -> (Vec, std::ops::Range) { let mut rest_start = None; for (i, &pat) in pat_tup.iter().enumerate() { - if pat.is_rest(self.db.as_hir_db(), self.env.body()) && rest_start.replace(i).is_some() - { - let span = pat.lazy_span(self.env.body()); + if pat.is_rest(self.db.as_hir_db(), self.body()) && rest_start.replace(i).is_some() { + let span = pat.lazy_span(self.body()); FuncBodyDiagAccumulator::push( self.db, BodyDiag::DuplicatedRestPat(span.into()).into(), ); return ( - TyId::invalid(self.db, InvalidCause::Other), + self.fresh_tys_n(expected_len.unwrap_or(0)), Range::default(), ); } } - let mut make_args = |len: usize| { - (0..len) - .map(|_| self.table.new_var(TyVarUniverse::General, &Kind::Star)) - .collect::>() - }; - match rest_start { Some(rest_start) => { - let (base, expected_args) = expected.decompose_ty_app(self.db); - let expected_args_len = expected_args.len(); - let minimum_len = expected_args.len() - 1; + let expected_len = expected_len.unwrap_or(0); + let minimum_len = pat_tup.len() - 1; - if base.is_tuple(self.db) && minimum_len <= expected_args.len() { - let diff = expected_args_len - minimum_len; + if minimum_len <= expected_len { + let diff = expected_len - minimum_len; let range = rest_start..rest_start + diff; - let ty_args = make_args(expected_args_len); - (TyId::tuple_with_elems(self.db, &ty_args), range) + (self.fresh_tys_n(expected_len), range) } else { - let ty_args = make_args(minimum_len); - (TyId::tuple_with_elems(self.db, &ty_args), Range::default()) + (self.fresh_tys_n(minimum_len), Range::default()) } } - None => { - let ty_args = make_args(pat_tup.len()); - (TyId::tuple_with_elems(self.db, &ty_args), Range::default()) - } + None => (self.fresh_tys_n(pat_tup.len()), Range::default()), } } } @@ -209,10 +310,28 @@ impl<'db, 'env> PathResolver<'db, 'env> { } fn resolve_early_resolved_path(&self, early: EarlyResolvedPath) -> ResolvedPathInPat { + let hir_db = self.db.as_hir_db(); + + // Try to resolve the partially resolved path as an enum variant. + let early = match early { + EarlyResolvedPath::Partial { + res, + unresolved_from, + } if res.is_enum() && unresolved_from + 1 == self.path.len(hir_db) => { + let segments = &self.path.segments(hir_db)[unresolved_from..]; + let scope = res.scope().unwrap(); + resolve_segments_early(self.db, segments, scope) + } + + _ => early, + }; + match early { EarlyResolvedPath::Full(bucket) => self.resolve_bucket(bucket), + EarlyResolvedPath::Partial { .. } => { - let diag = TyLowerDiag::AssocTy(self.pat.lazy_span(self.env.body()).into()); + let span = self.path_span().into(); + let diag = TyLowerDiag::AssocTy(span); ResolvedPathInPat::Diag(FuncBodyDiag::Ty(diag.into())) } } @@ -247,7 +366,7 @@ impl<'db, 'env> PathResolver<'db, 'env> { _ => { let diag = BodyDiag::InvalidPathDomainInPat { - primary: self.span(), + primary: self.path_span().into(), resolved: res .scope() .and_then(|scope| scope.name_span(self.db.as_hir_db())), @@ -258,8 +377,19 @@ impl<'db, 'env> PathResolver<'db, 'env> { } } - fn span(&self) -> DynLazySpan { - self.pat.lazy_span(self.env.body()).into() + fn path_span(&self) -> LazyPathSpan { + let Partial::Present(pat_data) = self.pat.data(self.db.as_hir_db(), self.env.body()) else { + unreachable!() + }; + + let pat_span = self.pat.lazy_span(self.env.body()); + + match pat_data { + Pat::Path(_) => pat_span.into_path_pat().path(), + Pat::PathTuple(..) => pat_span.into_path_tuple_pat().path(), + Pat::Record(..) => pat_span.into_record_pat().path(), + _ => unreachable!(), + } } } diff --git a/crates/hir-analysis/src/ty/ty_def.rs b/crates/hir-analysis/src/ty/ty_def.rs index 8eff63f39..19af386a1 100644 --- a/crates/hir-analysis/src/ty/ty_def.rs +++ b/crates/hir-analysis/src/ty/ty_def.rs @@ -468,8 +468,9 @@ pub struct AdtDef { param_set: GenericParamTypeSet, /// Fields of the ADT, if the ADT is an enum, this represents variants. + /// Otherwise, `fields[0]` represents all fields of the struct. #[return_ref] - pub fields: Vec, + pub fields: Vec, } impl AdtDef { @@ -608,7 +609,7 @@ impl FuncDef { /// This struct represents a field of an ADT. If the ADT is an enum, this /// represents a variant. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct AdtField { +pub struct AdtFieldList { name: Partial, /// Fields of the variant. @@ -621,7 +622,7 @@ pub struct AdtField { scope: ScopeId, } -impl AdtField { +impl AdtFieldList { pub fn ty(&self, db: &dyn HirAnalysisDb, i: usize) -> TyId { if let Some(ty) = self.tys[i].to_opt() { lower_hir_ty(db, ty, self.scope) @@ -807,7 +808,7 @@ pub enum TyVarUniverse { impl TyVar { pub(super) fn pretty_print(&self) -> String { match self.universe { - TyVarUniverse::General => format!("${}", self.key.0), + TyVarUniverse::General => ("_").to_string(), TyVarUniverse::Integral => "".to_string(), TyVarUniverse::String(n) => format!("String<{}>", n).to_string(), } diff --git a/crates/hir-analysis/src/ty/ty_lower.rs b/crates/hir-analysis/src/ty/ty_lower.rs index 74fd0751c..dc4dbbba8 100644 --- a/crates/hir-analysis/src/ty/ty_lower.rs +++ b/crates/hir-analysis/src/ty/ty_lower.rs @@ -13,7 +13,7 @@ use salsa::function::Configuration; use super::{ const_ty::{ConstTyData, ConstTyId}, ty_def::{ - AdtDef, AdtField, AdtRef, AdtRefId, FuncDef, InvalidCause, Kind, TyData, TyId, TyParam, + AdtDef, AdtFieldList, AdtRef, AdtRefId, FuncDef, InvalidCause, Kind, TyData, TyId, TyParam, }, }; use crate::{ @@ -449,7 +449,7 @@ struct AdtTyBuilder<'db> { db: &'db dyn HirAnalysisDb, adt: AdtRefId, params: GenericParamTypeSet, - variants: Vec, + variants: Vec, } impl<'db> AdtTyBuilder<'db> { @@ -499,7 +499,7 @@ impl<'db> AdtTyBuilder<'db> { let scope = self.adt.scope(self.db); fields.data(self.db.as_hir_db()).iter().for_each(|field| { - let variant = AdtField::new(field.name, vec![field.ty], scope); + let variant = AdtFieldList::new(field.name, vec![field.ty], scope); self.variants.push(variant); }) } @@ -524,7 +524,7 @@ impl<'db> AdtTyBuilder<'db> { VariantKind::Unit => vec![], }; - let variant = AdtField::new(variant.name, tys, scope); + let variant = AdtFieldList::new(variant.name, tys, scope); self.variants.push(variant) }) } diff --git a/crates/hir-analysis/test_files/ty_check/pat/path_tuple.fe b/crates/hir-analysis/test_files/ty_check/pat/path_tuple.fe new file mode 100644 index 000000000..f98552deb --- /dev/null +++ b/crates/hir-analysis/test_files/ty_check/pat/path_tuple.fe @@ -0,0 +1,10 @@ +pub enum Foo { + Variant(u8, u16, u32, u64) +} + +puf fn foo() { + let Foo::Variant(a, b, c, d) + let Foo::Variant(.., a, b) + let Foo::Variant(a, .., b, c) + let Foo::Variant(a, b, c, ..) +} \ No newline at end of file diff --git a/crates/hir-analysis/test_files/ty_check/pat/path_tuple.snap b/crates/hir-analysis/test_files/ty_check/pat/path_tuple.snap new file mode 100644 index 000000000..24febffc3 --- /dev/null +++ b/crates/hir-analysis/test_files/ty_check/pat/path_tuple.snap @@ -0,0 +1,132 @@ +--- +source: crates/hir-analysis/tests/ty_check.rs +expression: res +input_file: crates/hir-analysis/test_files/ty_check/pat/path_tuple.fe +--- +note: + ┌─ test_file.fe:5:14 + │ + 5 │ puf fn foo() { + │ ╭──────────────^ + 6 │ │ let Foo::Variant(a, b, c, d) + 7 │ │ let Foo::Variant(.., a, b) + 8 │ │ let Foo::Variant(a, .., b, c) + 9 │ │ let Foo::Variant(a, b, c, ..) +10 │ │ } + │ ╰─^ () + +note: + ┌─ test_file.fe:6:9 + │ +6 │ let Foo::Variant(a, b, c, d) + │ ^^^^^^^^^^^^^^^^^^^^^^^^ Foo + +note: + ┌─ test_file.fe:6:22 + │ +6 │ let Foo::Variant(a, b, c, d) + │ ^ u8 + +note: + ┌─ test_file.fe:6:25 + │ +6 │ let Foo::Variant(a, b, c, d) + │ ^ u16 + +note: + ┌─ test_file.fe:6:28 + │ +6 │ let Foo::Variant(a, b, c, d) + │ ^ u32 + +note: + ┌─ test_file.fe:6:31 + │ +6 │ let Foo::Variant(a, b, c, d) + │ ^ u64 + +note: + ┌─ test_file.fe:7:9 + │ +7 │ let Foo::Variant(.., a, b) + │ ^^^^^^^^^^^^^^^^^^^^^^ Foo + +note: + ┌─ test_file.fe:7:22 + │ +7 │ let Foo::Variant(.., a, b) + │ ^^ + +note: + ┌─ test_file.fe:7:26 + │ +7 │ let Foo::Variant(.., a, b) + │ ^ u32 + +note: + ┌─ test_file.fe:7:29 + │ +7 │ let Foo::Variant(.., a, b) + │ ^ u64 + +note: + ┌─ test_file.fe:8:9 + │ +8 │ let Foo::Variant(a, .., b, c) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Foo + +note: + ┌─ test_file.fe:8:22 + │ +8 │ let Foo::Variant(a, .., b, c) + │ ^ u8 + +note: + ┌─ test_file.fe:8:25 + │ +8 │ let Foo::Variant(a, .., b, c) + │ ^^ + +note: + ┌─ test_file.fe:8:29 + │ +8 │ let Foo::Variant(a, .., b, c) + │ ^ u32 + +note: + ┌─ test_file.fe:8:32 + │ +8 │ let Foo::Variant(a, .., b, c) + │ ^ u64 + +note: + ┌─ test_file.fe:9:9 + │ +9 │ let Foo::Variant(a, b, c, ..) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Foo + +note: + ┌─ test_file.fe:9:22 + │ +9 │ let Foo::Variant(a, b, c, ..) + │ ^ u8 + +note: + ┌─ test_file.fe:9:25 + │ +9 │ let Foo::Variant(a, b, c, ..) + │ ^ u16 + +note: + ┌─ test_file.fe:9:28 + │ +9 │ let Foo::Variant(a, b, c, ..) + │ ^ u32 + +note: + ┌─ test_file.fe:9:31 + │ +9 │ let Foo::Variant(a, b, c, ..) + │ ^^ + + diff --git a/crates/hir-analysis/test_files/ty_check/pat/tuple.fe b/crates/hir-analysis/test_files/ty_check/pat/tuple.fe new file mode 100644 index 000000000..34ebd2b03 --- /dev/null +++ b/crates/hir-analysis/test_files/ty_check/pat/tuple.fe @@ -0,0 +1,6 @@ +fn foo() { + let (x, y, z): (i8, u16, u32) + let (.., x, y): (u8, u16, u32, u64, u128) + let (x, y, z, ..): (u8, u16, u32, u64, u128) + let (x, .., y, z): (u8, u16, u32, u64, u128) +} \ No newline at end of file diff --git a/crates/hir-analysis/test_files/ty_check/pat/tuple.snap b/crates/hir-analysis/test_files/ty_check/pat/tuple.snap new file mode 100644 index 000000000..a8d071abf --- /dev/null +++ b/crates/hir-analysis/test_files/ty_check/pat/tuple.snap @@ -0,0 +1,126 @@ +--- +source: crates/hir-analysis/tests/ty_check.rs +expression: res +input_file: crates/hir-analysis/test_files/ty_check/pat/tuple.fe +--- +note: + ┌─ test_file.fe:1:10 + │ +1 │ fn foo() { + │ ╭──────────^ +2 │ │ let (x, y, z): (i8, u16, u32) +3 │ │ let (.., x, y): (u8, u16, u32, u64, u128) +4 │ │ let (x, y, z, ..): (u8, u16, u32, u64, u128) +5 │ │ let (x, .., y, z): (u8, u16, u32, u64, u128) +6 │ │ } + │ ╰─^ () + +note: + ┌─ test_file.fe:2:9 + │ +2 │ let (x, y, z): (i8, u16, u32) + │ ^^^^^^^^^ (i8, u16, u32) + +note: + ┌─ test_file.fe:2:10 + │ +2 │ let (x, y, z): (i8, u16, u32) + │ ^ i8 + +note: + ┌─ test_file.fe:2:13 + │ +2 │ let (x, y, z): (i8, u16, u32) + │ ^ u16 + +note: + ┌─ test_file.fe:2:16 + │ +2 │ let (x, y, z): (i8, u16, u32) + │ ^ u32 + +note: + ┌─ test_file.fe:3:9 + │ +3 │ let (.., x, y): (u8, u16, u32, u64, u128) + │ ^^^^^^^^^^ (u8, u16, u32, u64, u128) + +note: + ┌─ test_file.fe:3:10 + │ +3 │ let (.., x, y): (u8, u16, u32, u64, u128) + │ ^^ + +note: + ┌─ test_file.fe:3:14 + │ +3 │ let (.., x, y): (u8, u16, u32, u64, u128) + │ ^ u64 + +note: + ┌─ test_file.fe:3:17 + │ +3 │ let (.., x, y): (u8, u16, u32, u64, u128) + │ ^ u128 + +note: + ┌─ test_file.fe:4:9 + │ +4 │ let (x, y, z, ..): (u8, u16, u32, u64, u128) + │ ^^^^^^^^^^^^^ (u8, u16, u32, u64, u128) + +note: + ┌─ test_file.fe:4:10 + │ +4 │ let (x, y, z, ..): (u8, u16, u32, u64, u128) + │ ^ u8 + +note: + ┌─ test_file.fe:4:13 + │ +4 │ let (x, y, z, ..): (u8, u16, u32, u64, u128) + │ ^ u16 + +note: + ┌─ test_file.fe:4:16 + │ +4 │ let (x, y, z, ..): (u8, u16, u32, u64, u128) + │ ^ u32 + +note: + ┌─ test_file.fe:4:19 + │ +4 │ let (x, y, z, ..): (u8, u16, u32, u64, u128) + │ ^^ + +note: + ┌─ test_file.fe:5:9 + │ +5 │ let (x, .., y, z): (u8, u16, u32, u64, u128) + │ ^^^^^^^^^^^^^ (u8, u16, u32, u64, u128) + +note: + ┌─ test_file.fe:5:10 + │ +5 │ let (x, .., y, z): (u8, u16, u32, u64, u128) + │ ^ u8 + +note: + ┌─ test_file.fe:5:13 + │ +5 │ let (x, .., y, z): (u8, u16, u32, u64, u128) + │ ^^ + +note: + ┌─ test_file.fe:5:17 + │ +5 │ let (x, .., y, z): (u8, u16, u32, u64, u128) + │ ^ u64 + +note: + ┌─ test_file.fe:5:20 + │ +5 │ let (x, .., y, z): (u8, u16, u32, u64, u128) + │ ^ u128 + + diff --git a/crates/hir-analysis/tests/ty_check.rs b/crates/hir-analysis/tests/ty_check.rs index 232eeba62..7cbb9d78e 100644 --- a/crates/hir-analysis/tests/ty_check.rs +++ b/crates/hir-analysis/tests/ty_check.rs @@ -8,7 +8,7 @@ use test_db::HirAnalysisTestDb; #[dir_test( dir: "$CARGO_MANIFEST_DIR/test_files/ty_check", - glob: "*.fe" + glob: "**/*.fe" )] fn test_standalone(fixture: Fixture<&str>) { let mut db = HirAnalysisTestDb::default(); diff --git a/crates/hir/src/span/pat.rs b/crates/hir/src/span/pat.rs index a5052d996..2607b3428 100644 --- a/crates/hir/src/span/pat.rs +++ b/crates/hir/src/span/pat.rs @@ -1,16 +1,15 @@ use parser::ast; +use super::{ + body_source_map, define_lazy_span_node, + transition::{ChainInitiator, ResolvedOrigin, SpanTransitionChain}, +}; use crate::{ hir_def::{Body, PatId}, span::{path::LazyPathSpan, LazyLitSpan}, SpannedHirDb, }; -use super::{ - body_source_map, define_lazy_span_node, - transition::{ChainInitiator, ResolvedOrigin, SpanTransitionChain}, -}; - define_lazy_span_node!(LazyPatSpan, ast::Pat,); impl LazyPatSpan { pub fn new(body: Body, pat: PatId) -> Self { @@ -26,8 +25,8 @@ impl LazyPatSpan { LazyLitPatSpan(self.0) } - pub fn into_path_tuple_pat(self) -> LazyPathPatSpan { - LazyPathPatSpan(self.0) + pub fn into_path_tuple_pat(self) -> LazyPathTuplePatSpan { + LazyPathTuplePatSpan(self.0) } pub fn into_record_pat(self) -> LazyRecordPatSpan { diff --git a/crates/uitest/fixtures/ty_check/pat/path_tuple.fe b/crates/uitest/fixtures/ty_check/pat/path_tuple.fe new file mode 100644 index 000000000..3c73a90bc --- /dev/null +++ b/crates/uitest/fixtures/ty_check/pat/path_tuple.fe @@ -0,0 +1,10 @@ +pub enum Foo { + Variant(i32, u32) +} + +fn foo() { + let Foo::Variant(x, y, z) + let Foo::Variant(.., x, y, z) + let Foo::Variant(x, .., y, z) + let Foo::Variant(x, y, z, ..) +} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty_check/pat/path_tuple.snap b/crates/uitest/fixtures/ty_check/pat/path_tuple.snap new file mode 100644 index 000000000..113be3efb --- /dev/null +++ b/crates/uitest/fixtures/ty_check/pat/path_tuple.snap @@ -0,0 +1,30 @@ +--- +source: crates/uitest/tests/ty_check.rs +expression: diags +input_file: crates/uitest/fixtures/ty_check/pat/path_tuple.fe +--- +error[8-0006]: field count mismatch + ┌─ path_tuple.fe:6:9 + │ +6 │ let Foo::Variant(x, y, z) + │ ^^^^^^^^^^^^^^^^^^^^^ expected 2 fields here, but 3 given + +error[8-0006]: field count mismatch + ┌─ path_tuple.fe:7:9 + │ +7 │ let Foo::Variant(.., x, y, z) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields here, but 3 given + +error[8-0006]: field count mismatch + ┌─ path_tuple.fe:8:9 + │ +8 │ let Foo::Variant(x, .., y, z) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields here, but 3 given + +error[8-0006]: field count mismatch + ┌─ path_tuple.fe:9:9 + │ +9 │ let Foo::Variant(x, y, z, ..) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields here, but 3 given + + diff --git a/crates/uitest/fixtures/ty_check/pat/tuple.fe b/crates/uitest/fixtures/ty_check/pat/tuple.fe new file mode 100644 index 000000000..e405ede6d --- /dev/null +++ b/crates/uitest/fixtures/ty_check/pat/tuple.fe @@ -0,0 +1,6 @@ +fn foo() { + let (x, y, z): (i32, u32) + let (.., x, y, z): (i32, u32) + let (x, y, .., z): (i32, u32) + let (x, y, z, ..): (i32, u32) +} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty_check/pat/tuple.snap b/crates/uitest/fixtures/ty_check/pat/tuple.snap new file mode 100644 index 000000000..5f0bfadfa --- /dev/null +++ b/crates/uitest/fixtures/ty_check/pat/tuple.snap @@ -0,0 +1,30 @@ +--- +source: crates/uitest/tests/ty_check.rs +expression: diags +input_file: crates/uitest/fixtures/ty_check/pat/tuple.fe +--- +error[8-0000]: type mismatch + ┌─ tuple.fe:2:9 + │ +2 │ let (x, y, z): (i32, u32) + │ ^^^^^^^^^ expected `(i32, u32)`, but `(_, _, _)` is given + +error[8-0000]: type mismatch + ┌─ tuple.fe:3:9 + │ +3 │ let (.., x, y, z): (i32, u32) + │ ^^^^^^^^^^^^^ expected `(i32, u32)`, but `(_, _, _)` is given + +error[8-0000]: type mismatch + ┌─ tuple.fe:4:9 + │ +4 │ let (x, y, .., z): (i32, u32) + │ ^^^^^^^^^^^^^ expected `(i32, u32)`, but `(_, _, _)` is given + +error[8-0000]: type mismatch + ┌─ tuple.fe:5:9 + │ +5 │ let (x, y, z, ..): (i32, u32) + │ ^^^^^^^^^^^^^ expected `(i32, u32)`, but `(_, _, _)` is given + + diff --git a/crates/uitest/tests/ty_check.rs b/crates/uitest/tests/ty_check.rs index 67b1f6ed6..138e0d1a9 100644 --- a/crates/uitest/tests/ty_check.rs +++ b/crates/uitest/tests/ty_check.rs @@ -6,7 +6,7 @@ use fe_compiler_test_utils::snap_test; #[dir_test( dir: "$CARGO_MANIFEST_DIR/fixtures/ty_check", - glob: "*.fe" + glob: "**/*.fe" )] fn run_ty_check(fixture: Fixture<&str>) { let mut driver = DriverDataBase::default();