diff --git a/crates/driver2/src/diagnostics.rs b/crates/driver2/src/diagnostics.rs index cabede531..607ebfc4b 100644 --- a/crates/driver2/src/diagnostics.rs +++ b/crates/driver2/src/diagnostics.rs @@ -2,12 +2,11 @@ use std::ops::Range; use camino::Utf8Path; use codespan_reporting as cs; -use cs::{diagnostic as cs_diag, files as cs_files}; - use common::{ diagnostics::{LabelStyle, Severity}, InputDb, InputFile, }; +use cs::{diagnostic as cs_diag, files as cs_files}; use hir::diagnostics::DiagnosticVoucher; use crate::{DriverDataBase, DriverDb}; diff --git a/crates/hir-analysis/src/ty/diagnostics.rs b/crates/hir-analysis/src/ty/diagnostics.rs index b796f53b2..66699c0c0 100644 --- a/crates/hir-analysis/src/ty/diagnostics.rs +++ b/crates/hir-analysis/src/ty/diagnostics.rs @@ -13,6 +13,7 @@ use itertools::Itertools; use super::{ constraint::PredicateId, + ty_check::pat::ResolvedPatData, ty_def::{Kind, TyId}, }; use crate::HirAnalysisDb; @@ -30,12 +31,26 @@ pub struct FuncDefDiagAccumulator(pub(super) TyDiagCollection); #[salsa::accumulator] pub struct TypeAliasDefDiagAccumulator(pub(super) TyDiagCollection); #[salsa::accumulator] -pub struct FuncBodyDiagAccumulator(pub(super) TyDiagCollection); +pub struct FuncBodyDiagAccumulator(pub(super) FuncBodyDiag); + +#[derive(Debug, PartialEq, Eq, Hash, Clone, derive_more::From)] +pub enum FuncBodyDiag { + Ty(TyDiagCollection), + Body(BodyDiag), +} + +impl FuncBodyDiag { + pub(super) fn to_voucher(&self) -> Box { + match self { + Self::Ty(diag) => diag.to_voucher(), + Self::Body(diag) => Box::new(diag.clone()) as _, + } + } +} #[derive(Debug, PartialEq, Eq, Hash, Clone, derive_more::From)] pub enum TyDiagCollection { Ty(TyLowerDiag), - BodyDiag(BodyDiag), Satisfaction(TraitConstraintDiag), TraitLower(TraitLowerDiag), Impl(ImplDiag), @@ -45,7 +60,6 @@ impl TyDiagCollection { pub(super) fn to_voucher(&self) -> Box { match self.clone() { TyDiagCollection::Ty(diag) => Box::new(diag) as _, - TyDiagCollection::BodyDiag(diag) => Box::new(diag) as _, TyDiagCollection::Satisfaction(diag) => Box::new(diag) as _, TyDiagCollection::TraitLower(diag) => Box::new(diag) as _, TyDiagCollection::Impl(diag) => Box::new(diag) as _, @@ -527,6 +541,15 @@ pub enum BodyDiag { TypeMismatch(DynLazySpan, String, String), InfiniteOccurrence(DynLazySpan), DuplicatedRestPat(DynLazySpan), + InvalidPathDomainInPat { + primary: DynLazySpan, + resolved: Option, + }, + UnitVariantExpectedInPat { + primary: DynLazySpan, + kind: &'static str, + hint: String, + }, } impl BodyDiag { @@ -541,11 +564,27 @@ impl BodyDiag { Self::TypeMismatch(span, expected, actual) } + fn unit_variant_expected_in_pat( + db: &dyn HirAnalysisDb, + primary: DynLazySpan, + data: ResolvedPatData, + ) -> Self { + let kind = data.data_kind(db); + let hint = data.hint(db); + Self::UnitVariantExpectedInPat { + primary, + kind, + hint, + } + } + fn local_code(&self) -> u16 { match self { Self::TypeMismatch(_, _, _) => 0, Self::InfiniteOccurrence(_) => 1, Self::DuplicatedRestPat(_) => 2, + Self::InvalidPathDomainInPat { .. } => 3, + Self::UnitVariantExpectedInPat { .. } => 4, } } @@ -554,6 +593,8 @@ impl BodyDiag { Self::TypeMismatch(_, _, _) => "type mismatch".to_string(), Self::InfiniteOccurrence(_) => "infinite sized type found".to_string(), Self::DuplicatedRestPat(_) => "duplicated `..` found".to_string(), + Self::InvalidPathDomainInPat { .. } => "invalid item is given here".to_string(), + Self::UnitVariantExpectedInPat { .. } => "unit variant is expected here".to_string(), } } @@ -576,6 +617,41 @@ impl BodyDiag { "`..` can be used only once".to_string(), span.resolve(db), )], + + Self::InvalidPathDomainInPat { primary, resolved } => { + let mut diag = vec![SubDiagnostic::new( + LabelStyle::Primary, + "expected type or enum variant here".to_string(), + primary.resolve(db), + )]; + if let Some(resolved) = resolved { + diag.push(SubDiagnostic::new( + LabelStyle::Secondary, + "this item given".to_string(), + resolved.resolve(db), + )) + } + diag + } + + Self::UnitVariantExpectedInPat { + primary, + kind, + hint, + } => { + vec![ + SubDiagnostic::new( + LabelStyle::Primary, + format!("expected unit variant here, but found {}", kind,), + primary.resolve(db), + ), + SubDiagnostic::new( + LabelStyle::Secondary, + format!("Use {} instead", hint), + primary.resolve(db), + ), + ] + } } } diff --git a/crates/hir-analysis/src/ty/ty_check/env.rs b/crates/hir-analysis/src/ty/ty_check/env.rs index 8134e79ec..08d87565a 100644 --- a/crates/hir-analysis/src/ty/ty_check/env.rs +++ b/crates/hir-analysis/src/ty/ty_check/env.rs @@ -10,11 +10,12 @@ use crate::{ ty::{ ty_def::{InvalidCause, TyId}, ty_lower::lower_hir_ty, + unify::UnificationTable, }, HirAnalysisDb, }; -pub(super) struct ThCheckEnv<'db> { +pub(super) struct TyCheckEnv<'db> { db: &'db dyn HirAnalysisDb, body: Body, @@ -24,7 +25,7 @@ pub(super) struct ThCheckEnv<'db> { var_env: Vec, } -impl<'db> ThCheckEnv<'db> { +impl<'db> TyCheckEnv<'db> { pub(super) fn new_with_func(db: &'db dyn HirAnalysisDb, func: Func) -> Result { let hir_db = db.as_hir_db(); let Some(body) = func.body(hir_db) else { @@ -45,7 +46,7 @@ impl<'db> ThCheckEnv<'db> { return Err(()); }; - for param in params.data(hir_db) { + for (i, param) in params.data(hir_db).iter().enumerate() { let Some(name) = param.name() else { continue; }; @@ -58,8 +59,9 @@ impl<'db> ThCheckEnv<'db> { if ty.is_star_kind(db) { ty = TyId::invalid(db, InvalidCause::Other); } + let var = VarKind::Param(i, ty); - env.register_var(name, ty); + env.var_env.last_mut().unwrap().register_var(name, var); } Ok(env) @@ -94,12 +96,21 @@ impl<'db> ThCheckEnv<'db> { self.pat_ty.insert(pat, ty); } - pub(super) fn register_var(&mut self, name: IdentId, ty: TyId) { + pub(super) fn register_var(&mut self, name: IdentId, pat: PatId) { let var_env = self.var_env.last_mut().unwrap(); - var_env.vars.insert(name, ty); + let var = VarKind::Local(pat); + var_env.register_var(name, var); } - pub(super) fn finish(self) -> TypedBody { + pub(super) fn finish(mut self, table: &mut UnificationTable) -> TypedBody { + self.expr_ty + .values_mut() + .for_each(|ty| *ty = ty.apply_subst(self.db, table)); + + self.pat_ty + .values_mut() + .for_each(|ty| *ty = ty.apply_subst(self.db, table)); + TypedBody { body: Some(self.body), pat_ty: self.pat_ty, @@ -114,11 +125,19 @@ impl<'db> ThCheckEnv<'db> { pub(super) fn stmt_data(&self, stmt: StmtId) -> &'db Partial { stmt.data(self.db.as_hir_db(), self.body) } + + pub(super) fn scope(&self) -> ScopeId { + self.var_env.last().unwrap().scope + } + + pub(super) fn iter_block_env(&self) -> impl Iterator { + self.var_env.iter().rev() + } } -struct BlockEnv { - scope: ScopeId, - vars: FxHashMap, +pub(super) struct BlockEnv { + pub(super) scope: ScopeId, + pub(super) vars: FxHashMap, } impl BlockEnv { @@ -128,4 +147,18 @@ impl BlockEnv { vars: FxHashMap::default(), } } + + fn register_var(&mut self, name: IdentId, var: VarKind) { + self.vars.insert(name, var); + } + + pub(super) fn lookup_var(&self, name: IdentId) -> Option { + self.vars.get(&name).copied() + } +} + +#[derive(Clone, Copy)] +pub(super) enum VarKind { + Local(PatId), + Param(usize, TyId), } diff --git a/crates/hir-analysis/src/ty/ty_check/mod.rs b/crates/hir-analysis/src/ty/ty_check/mod.rs index b63b0bf4e..9aabc8038 100644 --- a/crates/hir-analysis/src/ty/ty_check/mod.rs +++ b/crates/hir-analysis/src/ty/ty_check/mod.rs @@ -1,23 +1,21 @@ +#![allow(unused)] mod env; +pub(crate) mod pat; -use std::ops::Range; - -use env::ThCheckEnv; +use env::TyCheckEnv; use hir::{ - hir_def::{ - Body, Expr, ExprId, Func, IdentId, LitKind, Partial, Pat, PatId, PathId, Stmt, StmtId, - }, + hir_def::{Body, Expr, ExprId, Func, LitKind, Partial, PatId, Stmt, StmtId}, span::DynLazySpan, }; use rustc_hash::FxHashMap; use super::{ diagnostics::{BodyDiag, FuncBodyDiagAccumulator}, - ty_def::{AdtDef, InvalidCause, Kind, TyId, TyVar, TyVarUniverse}, + ty_def::{InvalidCause, Kind, TyId, TyVarUniverse}, ty_lower::lower_hir_ty, unify::{UnificationError, UnificationTable}, }; -use crate::{name_resolution::resolve_path_early, HirAnalysisDb}; +use crate::HirAnalysisDb; #[salsa::tracked(return_ref)] pub fn check_func_body(db: &dyn HirAnalysisDb, func: Func) -> TypedBody { @@ -26,20 +24,19 @@ pub fn check_func_body(db: &dyn HirAnalysisDb, func: Func) -> TypedBody { }; checker.run(); - checker.finish() } pub struct TyChecker<'db> { db: &'db dyn HirAnalysisDb, - env: ThCheckEnv<'db>, + env: TyCheckEnv<'db>, table: UnificationTable<'db>, expected: TyId, } impl<'db> TyChecker<'db> { fn new_with_func(db: &'db dyn HirAnalysisDb, func: Func) -> Result { - let env = ThCheckEnv::new_with_func(db, func)?; + let env = TyCheckEnv::new_with_func(db, func)?; let expected_ty = match func.ret_ty(db.as_hir_db()) { Some(hir_ty) => { let ty = lower_hir_ty(db, hir_ty, func.scope()); @@ -60,11 +57,12 @@ impl<'db> TyChecker<'db> { self.check_expr_ty(root_expr, self.expected); } - fn finish(self) -> TypedBody { - self.env.finish() + fn finish(mut self) -> TypedBody { + // TODO: check for untyped expressions and patterns. + self.env.finish(&mut self.table) } - fn new(db: &'db dyn HirAnalysisDb, env: ThCheckEnv<'db>, expected: TyId) -> Self { + fn new(db: &'db dyn HirAnalysisDb, env: TyCheckEnv<'db>, expected: TyId) -> Self { let table = UnificationTable::new(db); Self { db, @@ -134,118 +132,6 @@ impl<'db> TyChecker<'db> { } } - 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 actual = TyId::invalid(self.db, InvalidCause::Other); - return self.unify_ty(pat, actual, expected); - }; - - 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(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::Tuple(pat_tup) => { - let (actual, rest_range) = self.unpack_rest_pat(pat_tup, expected, pat); - 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; - } - - 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; - } - - self.check_pat(pat_tup[i], pat_ty); - } - - unified - } - - Pat::Path(path) => todo!(), - - Pat::PathTuple(path, tup) => todo!(), - - Pat::Record(path, fields) => todo!(), - - Pat::Or(lhs, rhs) => { - self.check_pat(*lhs, expected); - self.check_pat(*rhs, expected) - } - } - } - - fn unpack_rest_pat( - &mut self, - pat_tup: &[PatId], - expected: TyId, - pat: PatId, - ) -> (TyId, 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()); - FuncBodyDiagAccumulator::push( - self.db, - BodyDiag::DuplicatedRestPat(span.into()).into(), - ); - return ( - TyId::invalid(self.db, InvalidCause::Other), - 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; - - if base.is_tuple(self.db) && minimum_len <= expected_args.len() { - let diff = expected_args_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) - } else { - let ty_args = make_args(minimum_len); - (TyId::tuple_with_elems(self.db, &ty_args), Range::default()) - } - } - - None => { - let ty_args = make_args(pat_tup.len()); - (TyId::tuple_with_elems(self.db, &ty_args), Range::default()) - } - } - } - fn lit_ty(&mut self, lit: &LitKind) -> TyId { match lit { LitKind::Bool(_) => TyId::bool(self.db), @@ -265,7 +151,7 @@ impl<'db> TyChecker<'db> { let t = t.into(); let actual = match self.table.unify(expected, actual) { - Ok(()) => actual.apply_subst(self.db, &mut self.table), + Ok(()) => actual, Err(UnificationError::TypeMismatch) => { let actual = actual.apply_subst(self.db, &mut self.table); @@ -347,16 +233,3 @@ impl Typeable { } } } - -#[derive(Clone)] -enum ResolvedPath { - Adt(ResolvedAdt), - NewVar(IdentId), - NotFound, -} - -#[derive(Clone)] -struct ResolvedAdt { - adt: AdtDef, - variant_idx: Option, -} diff --git a/crates/hir-analysis/src/ty/ty_check/pat.rs b/crates/hir-analysis/src/ty/ty_check/pat.rs new file mode 100644 index 000000000..380f6b8c5 --- /dev/null +++ b/crates/hir-analysis/src/ty/ty_check/pat.rs @@ -0,0 +1,289 @@ +use std::ops::Range; + +use hir::{ + hir_def::{ + scope_graph::ScopeId, Enum, IdentId, ItemKind, Partial, Pat, PatId, PathId, + VariantKind as HirVariantKind, + }, + span::DynLazySpan, +}; + +use super::{env::TyCheckEnv, TyChecker}; +use crate::{ + name_resolution::{ + resolve_path_early, EarlyResolvedPath, NameDomain, NameRes, NameResBucket, NameResKind, + }, + ty::{ + diagnostics::{BodyDiag, FuncBodyDiag, FuncBodyDiagAccumulator, TyLowerDiag}, + ty_def::{AdtDef, AdtRef, AdtRefId, InvalidCause, Kind, TyId, TyVarUniverse}, + ty_lower::lower_adt, + }, + HirAnalysisDb, +}; + +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 actual = TyId::invalid(self.db, InvalidCause::Other); + return self.unify_ty(pat, actual, expected); + }; + + 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(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::Tuple(pat_tup) => { + let (actual, rest_range) = self.unpack_rest_pat(pat_tup, expected, pat); + 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; + } + + 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; + } + + self.check_pat(pat_tup[i], pat_ty); + } + + unified + } + + Pat::Path(path) => { + 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(adt) => { + todo!() + } + + ResolvedPathInPat::NewBinding(binding) => { + todo!() + } + ResolvedPathInPat::Diag(diag) => { + FuncBodyDiagAccumulator::push(self.db, diag); + TyId::invalid(self.db, InvalidCause::Other) + } + + ResolvedPathInPat::Invalid => TyId::invalid(self.db, InvalidCause::Other), + } + } + + Pat::PathTuple(path, tup) => todo!(), + + Pat::Record(path, fields) => todo!(), + + Pat::Or(lhs, rhs) => { + self.check_pat(*lhs, expected); + self.check_pat(*rhs, expected) + } + } + } + + fn unpack_rest_pat( + &mut self, + pat_tup: &[PatId], + expected: TyId, + pat: PatId, + ) -> (TyId, 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()); + FuncBodyDiagAccumulator::push( + self.db, + BodyDiag::DuplicatedRestPat(span.into()).into(), + ); + return ( + TyId::invalid(self.db, InvalidCause::Other), + 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; + + if base.is_tuple(self.db) && minimum_len <= expected_args.len() { + let diff = expected_args_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) + } else { + let ty_args = make_args(minimum_len); + (TyId::tuple_with_elems(self.db, &ty_args), Range::default()) + } + } + + None => { + let ty_args = make_args(pat_tup.len()); + (TyId::tuple_with_elems(self.db, &ty_args), Range::default()) + } + } + } +} + +fn resolve_path_in_pat( + db: &dyn HirAnalysisDb, + env: &TyCheckEnv, + path: PathId, + pat: PatId, +) -> ResolvedPathInPat { + PathResolver { db, env, pat }.resolve_path(path) +} + +struct PathResolver<'db, 'env> { + db: &'db dyn HirAnalysisDb, + env: &'env TyCheckEnv<'db>, + pat: PatId, +} + +impl<'db, 'env> PathResolver<'db, 'env> { + fn resolve_path(&self, path: PathId) -> ResolvedPathInPat { + let early_resolved_path = resolve_path_early(self.db, path, self.env.scope()); + let resolved = self.resolve_early_resolved_path(early_resolved_path); + + if !path.is_ident(self.db.as_hir_db()) { + resolved + } else { + match resolved { + ResolvedPathInPat::Diag(_) => { + if let Some(ident) = path.last_segment(self.db.as_hir_db()).to_opt() { + ResolvedPathInPat::NewBinding(ident) + } else { + resolved + } + } + + _ => resolved, + } + } + } + + fn resolve_early_resolved_path(&self, early: EarlyResolvedPath) -> ResolvedPathInPat { + match early { + EarlyResolvedPath::Full(bucket) => self.resolve_bucket(bucket), + EarlyResolvedPath::Partial { .. } => { + let diag = TyLowerDiag::AssocTy(self.pat.lazy_span(self.env.body()).into()); + ResolvedPathInPat::Diag(FuncBodyDiag::Ty(diag.into())) + } + } + } + + fn resolve_bucket(&self, mut bucket: NameResBucket) -> ResolvedPathInPat { + if let Ok(res) = bucket.pick(NameDomain::Value) { + let res = self.resolve_name_res(res); + if !matches!(res, ResolvedPathInPat::Diag(_) | ResolvedPathInPat::Invalid) { + return res; + } + } + + match bucket.pick(NameDomain::Type) { + Ok(res) => self.resolve_name_res(res), + Err(_) => ResolvedPathInPat::Invalid, + } + } + + fn resolve_name_res(&self, res: &NameRes) -> ResolvedPathInPat { + match res.kind { + NameResKind::Scope(ScopeId::Item(ItemKind::Struct(struct_))) => { + let adt = lower_adt(self.db, AdtRefId::from_struct(self.db, struct_)); + ResolvedPathInPat::Data(ResolvedPatData::Adt(adt)) + } + + NameResKind::Scope(ScopeId::Variant(parent, idx)) => { + let enum_: Enum = parent.try_into().unwrap(); + let data = ResolvedPatData::Variant(enum_, idx); + ResolvedPathInPat::Data(data) + } + + _ => { + let diag = BodyDiag::InvalidPathDomainInPat { + primary: self.span(), + resolved: res + .scope() + .and_then(|scope| scope.name_span(self.db.as_hir_db())), + }; + + ResolvedPathInPat::Diag(diag.into()) + } + } + } + + fn span(&self) -> DynLazySpan { + self.pat.lazy_span(self.env.body()).into() + } +} + +#[derive(Clone, Debug)] +pub(crate) enum ResolvedPathInPat { + Data(ResolvedPatData), + NewBinding(IdentId), + Diag(FuncBodyDiag), + Invalid, +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub(crate) enum ResolvedPatData { + Adt(AdtDef), + Variant(Enum, usize), +} + +impl ResolvedPatData { + pub(crate) fn adt_ref(&self, db: &dyn HirAnalysisDb) -> AdtRef { + match self { + ResolvedPatData::Adt(adt) => adt.adt_ref(db).data(db), + ResolvedPatData::Variant(enum_, _) => AdtRef::Enum(*enum_), + } + } + + pub(crate) fn data_kind(&self, db: &dyn HirAnalysisDb) -> &'static str { + match self { + ResolvedPatData::Adt(adt) => adt.adt_ref(db).kind_name(db), + ResolvedPatData::Variant(enum_, idx) => { + let hir_db = db.as_hir_db(); + match enum_.variants(hir_db).data(hir_db)[*idx].kind { + hir::hir_def::VariantKind::Unit => "unit variant", + HirVariantKind::Tuple(_) => "tuple variant", + HirVariantKind::Record(_) => "record variant", + } + } + } + } + + pub(crate) fn hint(&self, db: &dyn HirAnalysisDb) -> String { + todo!() + } +} diff --git a/crates/hir-analysis/src/ty/ty_def.rs b/crates/hir-analysis/src/ty/ty_def.rs index 4319f1774..8cae5a376 100644 --- a/crates/hir-analysis/src/ty/ty_def.rs +++ b/crates/hir-analysis/src/ty/ty_def.rs @@ -397,29 +397,7 @@ impl TyId { } pub(super) fn from_hir_prim_ty(db: &dyn HirAnalysisDb, hir_prim: HirPrimTy) -> Self { - match hir_prim { - HirPrimTy::Bool => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::Bool))), - - HirPrimTy::Int(int_ty) => match int_ty { - HirIntTy::I8 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::I8))), - HirIntTy::I16 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::I16))), - HirIntTy::I32 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::I32))), - HirIntTy::I64 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::I64))), - HirIntTy::I128 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::I128))), - HirIntTy::I256 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::I256))), - }, - - HirPrimTy::Uint(uint_ty) => match uint_ty { - HirUintTy::U8 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::U8))), - HirUintTy::U16 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::U16))), - HirUintTy::U32 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::U32))), - HirUintTy::U64 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::U64))), - HirUintTy::U128 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::U128))), - HirUintTy::U256 => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::U256))), - }, - - HirPrimTy::String => Self::new(db, TyData::TyBase(TyBase::Prim(PrimTy::String))), - } + Self::new(db, TyData::TyBase(hir_prim.into())) } pub(super) fn const_ty_param(self, db: &dyn HirAnalysisDb) -> Option { @@ -865,7 +843,7 @@ impl TyParam { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, derive_more::From)] pub enum TyBase { Prim(PrimTy), Adt(AdtDef), @@ -941,6 +919,34 @@ impl TyBase { } } +impl From for TyBase { + fn from(hir_prim: HirPrimTy) -> Self { + match hir_prim { + HirPrimTy::Bool => Self::Prim(PrimTy::Bool), + + HirPrimTy::Int(int_ty) => match int_ty { + HirIntTy::I8 => Self::Prim(PrimTy::I8), + HirIntTy::I16 => Self::Prim(PrimTy::I16), + HirIntTy::I32 => Self::Prim(PrimTy::I32), + HirIntTy::I64 => Self::Prim(PrimTy::I64), + HirIntTy::I128 => Self::Prim(PrimTy::I128), + HirIntTy::I256 => Self::Prim(PrimTy::I256), + }, + + HirPrimTy::Uint(uint_ty) => match uint_ty { + HirUintTy::U8 => Self::Prim(PrimTy::U8), + HirUintTy::U16 => Self::Prim(PrimTy::U16), + HirUintTy::U32 => Self::Prim(PrimTy::U32), + HirUintTy::U64 => Self::Prim(PrimTy::U64), + HirUintTy::U128 => Self::Prim(PrimTy::U128), + HirUintTy::U256 => Self::Prim(PrimTy::U256), + }, + + HirPrimTy::String => Self::Prim(PrimTy::String), + } + } +} + #[derive(Debug, Clone, PartialEq, Copy, Eq, Hash)] pub enum PrimTy { Bool, @@ -1029,6 +1035,10 @@ impl AdtRefId { .unwrap_or_else(|| IdentId::new(hir_db, "".to_string())) } + pub fn kind_name(self, db: &dyn HirAnalysisDb) -> &'static str { + self.as_item(db).kind_name() + } + pub fn name_span(self, db: &dyn HirAnalysisDb) -> DynLazySpan { self.scope(db) .name_span(db.as_hir_db())