diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 00c7b95215..a8c4026e31 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs @@ -285,6 +285,7 @@ impl ExprValidator { let pattern_arena = Arena::new(); let cx = MatchCheckCtx { module: self.owner.module(db.upcast()), + body: self.owner, db, pattern_arena: &pattern_arena, }; diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs index acb3280329..84ded517ba 100644 --- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -53,7 +53,7 @@ use smallvec::{smallvec, SmallVec}; use stdx::never; use syntax::SmolStr; -use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind}; +use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind}; use super::{ usefulness::{helper::Captures, MatchCheckCtx, PatCtxt}, @@ -753,8 +753,8 @@ impl<'p> Fields<'p> { let fields_len = variant.variant_data(cx.db.upcast()).fields().len() as u32; (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| { - // TODO check ty has been normalized let ty = field_ty[fid].clone().substitute(Interner, substs); + let ty = normalize(cx.db, cx.body, ty); let is_visible = matches!(adt, hir_def::AdtId::EnumId(..)) || visibility[fid].is_visible_from(cx.db.upcast(), cx.module); let is_uninhabited = cx.is_uninhabited(&ty); diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs index 9b00c4dccf..e8a13955d2 100644 --- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs @@ -273,7 +273,7 @@ use std::iter::once; -use hir_def::{AdtId, HasModule, ModuleId}; +use hir_def::{AdtId, DefWithBodyId, HasModule, ModuleId}; use smallvec::{smallvec, SmallVec}; use typed_arena::Arena; @@ -285,6 +285,7 @@ use self::{helper::Captures, ArmType::*, Usefulness::*}; pub(crate) struct MatchCheckCtx<'a, 'p> { pub(crate) module: ModuleId, + pub(crate) body: DefWithBodyId, pub(crate) db: &'a dyn HirDatabase, /// Lowered patterns from arms plus generated by the check. pub(crate) pattern_arena: &'p Arena>, diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 1bc19323da..95d9a6735a 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -16,7 +16,7 @@ use std::ops::Index; use std::sync::Arc; -use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar}; +use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; use hir_def::{ body::Body, data::{ConstData, FunctionData, StaticData}, @@ -70,6 +70,26 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc Ty { + if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) { + return ty; + } + let krate = owner.module(db.upcast()).krate(); + let trait_env = owner + .as_generic_def_id() + .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); + let mut table = unify::InferenceTable::new(db, trait_env.clone()); + + let ty_with_vars = table.normalize_associated_types_in(ty); + table.resolve_obligations_as_possible(); + table.propagate_diverging_flag(); + table.resolve_completely(ty_with_vars) +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] enum ExprOrPatId { ExprId(ExprId), diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs index 5a35704697..5ef4d2a226 100644 --- a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs @@ -863,6 +863,30 @@ fn main() { ); } + #[test] + fn normalize_field_ty() { + check_diagnostics_no_bails( + r" +trait Trait { type Projection; } +enum E {Foo, Bar} +struct A; +impl Trait for A { type Projection = E; } +struct Next(T::Projection); +static __: () = { + let n: Next = Next(E::Foo); + match n { Next(E::Foo) => {} } + // ^ error: missing match arm + match n { Next(E::Foo | E::Bar) => {} } + match n { Next(E::Foo | _ ) => {} } + match n { Next(_ | E::Bar) => {} } + match n { _ | Next(E::Bar) => {} } + match &n { Next(E::Foo | E::Bar) => {} } + match &n { _ | Next(E::Bar) => {} } +};", + + ); + } + mod false_negatives { //! The implementation of match checking here is a work in progress. As we roll this out, we //! prefer false negatives to false positives (ideally there would be no false positives). This