From 678d85ca7e4d7e631a450b8c050fe7696da0cac3 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Thu, 29 Apr 2021 23:28:43 +0500 Subject: Implement struct ctor application --- crates/hir_def/src/path.rs | 2 +- crates/hir_ty/src/diagnostics/expr.rs | 2 +- crates/hir_ty/src/diagnostics/pattern.rs | 55 ++++++++- .../src/diagnostics/pattern/deconstruct_pat.rs | 123 ++++++++++++--------- .../hir_ty/src/diagnostics/pattern/usefulness.rs | 8 +- 5 files changed, 121 insertions(+), 69 deletions(-) diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 16440041d..4cdb5913d 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs @@ -166,7 +166,7 @@ impl Path { } /// Converts a known mod path to `Path`. - pub(crate) fn from_known_path( + pub fn from_known_path( path: ModPath, generic_args: Vec>>, ) -> Path { diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 88018b5d9..a62f0fa4f 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs @@ -382,7 +382,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { let pattern_arena = usefulness::PatternArena::clone_from(&body.pats); let cx = usefulness::MatchCheckCtx { - krate: self.owner.module(db.upcast()).krate(), + module: self.owner.module(db.upcast()), match_expr, body, infer: &infer, diff --git a/crates/hir_ty/src/diagnostics/pattern.rs b/crates/hir_ty/src/diagnostics/pattern.rs index 28c7a244d..4dcbd7f9f 100644 --- a/crates/hir_ty/src/diagnostics/pattern.rs +++ b/crates/hir_ty/src/diagnostics/pattern.rs @@ -11,24 +11,67 @@ mod tests { use super::*; #[test] - fn unit_exhaustive() { + fn unit() { check_diagnostics( r#" fn main() { - match () { () => {} } - match () { _ => {} } + match () { () => {} } + match () { _ => {} } + match () { } + //^^ Missing match arm } "#, ); } #[test] - fn unit_non_exhaustive() { + fn tuple_of_units() { check_diagnostics( r#" fn main() { - match () { } - //^^ Missing match arm + match ((), ()) { ((), ()) => {} } + match ((), ()) { ((), _) => {} } + match ((), ()) { (_, _) => {} } + match ((), ()) { _ => {} } + match ((), ()) { } + //^^^^^^^^ Missing match arm +} +"#, + ); + } + + #[test] + fn tuple_with_ellipsis() { + // TODO: test non-exhaustive match with ellipsis in the middle + // of a pattern, check reported witness + check_diagnostics( + r#" +struct A; struct B; +fn main(v: (A, (), B)) { + match v { (A, ..) => {} } + match v { (.., B) => {} } + match v { (A, .., B) => {} } + match v { (..) => {} } + match v { } + //^ Missing match arm +} +"#, + ); + } + + #[test] + fn strukt() { + check_diagnostics( + r#" +struct A; struct B; +struct S { a: A, b: B} +fn main(v: S) { + match v { S { a, b } => {} } + match v { S { a: _, b: _ } => {} } + match v { S { .. } => {} } + match v { _ => {} } + match v { } + //^ Missing match arm } "#, ); diff --git a/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs index c02e783af..7bd02a502 100644 --- a/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs @@ -1,5 +1,9 @@ use hir_def::{ - expr::{Pat, PatId}, + expr::{Pat, PatId, RecordFieldPat}, + find_path::find_path, + item_scope::ItemInNs, + path::Path, + type_ref::Mutability, AttrDefId, EnumVariantId, HasModule, VariantId, }; @@ -126,19 +130,25 @@ impl Constructor { /// Determines the constructor that the given pattern can be specialized to. pub(super) fn from_pat(cx: &MatchCheckCtx<'_>, pat: PatId) -> Self { + let ty = cx.type_of(pat); match &cx.pattern_arena.borrow()[pat] { Pat::Bind { .. } | Pat::Wild => Wildcard, Pat::Tuple { .. } | Pat::Ref { .. } | Pat::Box { .. } => Single, + Pat::Record { .. } | Pat::Path(_) | Pat::TupleStruct { .. } => { + let variant_id = + cx.infer.variant_resolution_for_pat(pat).unwrap_or_else(|| todo!()); + match variant_id { + VariantId::EnumVariantId(id) => Variant(id), + VariantId::StructId(_) | VariantId::UnionId(_) => Single, + } + } + Pat::Or(..) => panic!("bug: Or-pattern should have been expanded earlier on."), pat => todo!("Constructor::from_pat {:?}", pat), // Pat::Missing => {} - // Pat::Or(_) => {} - // Pat::Record { path, args, ellipsis } => {} // Pat::Range { start, end } => {} // Pat::Slice { prefix, slice, suffix } => {} - // Pat::Path(_) => {} // Pat::Lit(_) => {} - // Pat::TupleStruct { path, args, ellipsis } => {} // Pat::ConstBlock(_) => {} } } @@ -435,7 +445,8 @@ impl Fields { } else { let variant_id = constructor.variant_id_for_adt(*adt, cx); let variant = variant_id.variant_data(cx.db.upcast()); - let adt_is_local = variant_id.module(cx.db.upcast()).krate() == cx.krate; + let adt_is_local = + variant_id.module(cx.db.upcast()).krate() == cx.module.krate(); // Whether we must not match the fields of this variant exhaustively. let is_non_exhaustive = is_field_list_non_exhaustive(variant_id, cx) && !adt_is_local; @@ -490,43 +501,44 @@ impl Fields { Single | Variant(_) => match pcx.ty.kind(&Interner) { TyKind::Adt(..) | TyKind::Tuple(..) => { // We want the real indices here. - // TODO indices + // TODO indices and ellipsis interaction, tests let subpatterns = subpatterns_and_indices.iter().map(|&(_, pat)| pat).collect(); if let Some((adt, substs)) = pcx.ty.as_adt() { - if let hir_def::AdtId::EnumId(_) = adt { - todo!() - } else { - todo!() + let item = ItemInNs::Types(adt.into()); + let path = find_path(pcx.cx.db.upcast(), item, pcx.cx.module) + .map(|mpath| Path::from_known_path(mpath, Vec::new()).into()); + match adt { + hir_def::AdtId::EnumId(_) => todo!(), + hir_def::AdtId::StructId(id) => { + let variant_data = &pcx.cx.db.struct_data(id).variant_data; + let args = subpatterns_and_indices + .iter() + .zip(variant_data.fields().iter()) + .map(|(&(_, pat), (_, field_data))| RecordFieldPat { + name: field_data.name.clone(), + pat, + }) + .collect(); + Pat::Record { path, args, ellipsis: false } + } + hir_def::AdtId::UnionId(_) => todo!(), } } else { - // TODO ellipsis Pat::Tuple { args: subpatterns, ellipsis: None } } } - - _ => todo!(), - // TyKind::AssociatedType(_, _) => {} - // TyKind::Scalar(_) => {} - // TyKind::Array(_, _) => {} - // TyKind::Slice(_) => {} - // TyKind::Raw(_, _) => {} - // TyKind::Ref(_, _, _) => {} - // TyKind::OpaqueType(_, _) => {} - // TyKind::FnDef(_, _) => {} - // TyKind::Str => {} - // TyKind::Never => {} - // TyKind::Closure(_, _) => {} - // TyKind::Generator(_, _) => {} - // TyKind::GeneratorWitness(_, _) => {} - // TyKind::Foreign(_) => {} - // TyKind::Error => {} - // TyKind::Placeholder(_) => {} - // TyKind::Dyn(_) => {} - // TyKind::Alias(_) => {} - // TyKind::Function(_) => {} - // TyKind::BoundVar(_) => {} - // TyKind::InferenceVar(_, _) => {} + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to reconstruct the correct constant pattern here. However a string + // literal pattern will never be reported as a non-exhaustiveness witness, so we + // can ignore this issue. + TyKind::Ref(..) => { + Pat::Ref { pat: subpatterns.next().unwrap(), mutability: Mutability::Shared } + } + TyKind::Slice(..) | TyKind::Array(..) => { + panic!("bug: bad slice pattern {:?} {:?}", ctor, pcx.ty) + } + _ => Pat::Wild, }, Constructor::Slice(slice) => { todo!() @@ -537,9 +549,9 @@ impl Fields { NonExhaustive => Pat::Wild, Wildcard => Pat::Wild, Opaque => panic!("bug: we should not try to apply an opaque constructor"), - Missing => panic!( - "bug: trying to apply the `Missing` constructor; this should have been done in `apply_constructors`" - ), + Missing => { + panic!("bug: trying to apply the `Missing` constructor; this should have been done in `apply_constructors`") + } } } @@ -628,30 +640,31 @@ impl Fields { cx: &MatchCheckCtx<'_>, ) -> Self { match &cx.pattern_arena.borrow()[pat] { - Pat::Ref { pat: subpattern, .. } => { + Pat::Ref { pat: subpattern, .. } | Pat::Box { inner: subpattern } => { assert_eq!(self.len(), 1); Fields::from_single_pattern(*subpattern) } - Pat::Tuple { args: subpatterns, ellipsis } => { + Pat::Tuple { args, ellipsis } | Pat::TupleStruct { args, ellipsis, .. } => { // FIXME(iDawer) handle ellipsis. // XXX(iDawer): in rustc, this is handled by HIR->TypedHIR lowering // rustc_mir_build::thir::pattern::PatCtxt::lower_tuple_subpats(..) - self.replace_with_fieldpats(subpatterns.iter().copied()) + self.replace_with_fieldpats(args.iter().copied()) } - - Pat::Wild => self.clone(), - pat => todo!("Fields::replace_with_pattern_arguments({:?})", pat), - // Pat::Missing => {} - // Pat::Or(_) => {} - // Pat::Record { path, args, ellipsis } => {} - // Pat::Range { start, end } => {} - // Pat::Slice { prefix, slice, suffix } => {} - // Pat::Path(_) => {} - // Pat::Lit(_) => {} - // Pat::Bind { mode, name, subpat } => {} - // Pat::TupleStruct { path, args, ellipsis } => {} - // Pat::Box { inner } => {} - // Pat::ConstBlock(_) => {} + Pat::Record { args, ellipsis, .. } => { + // FIXME(iDawer) handle ellipsis. + self.replace_with_fieldpats(args.iter().map(|field_pat| field_pat.pat)) + } + Pat::Slice { .. } => { + todo!() + } + Pat::Missing + | Pat::Wild + | Pat::Or(_) + | Pat::Range { .. } + | Pat::Path(_) + | Pat::Lit(_) + | Pat::Bind { .. } + | Pat::ConstBlock(_) => self.clone(), } } } diff --git a/crates/hir_ty/src/diagnostics/pattern/usefulness.rs b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs index 2e5d2fb6c..4b55aee97 100644 --- a/crates/hir_ty/src/diagnostics/pattern/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs @@ -3,11 +3,7 @@ use std::{cell::RefCell, iter::FromIterator, ops::Index, sync::Arc}; -use base_db::CrateId; -use hir_def::{ - body::Body, - expr::{ExprId, Pat, PatId}, -}; +use hir_def::{ModuleId, body::Body, expr::{ExprId, Pat, PatId}}; use la_arena::Arena; use once_cell::unsync::OnceCell; use rustc_hash::FxHashMap; @@ -24,7 +20,7 @@ use self::{ }; pub(crate) struct MatchCheckCtx<'a> { - pub(crate) krate: CrateId, + pub(crate) module: ModuleId, pub(crate) match_expr: ExprId, pub(crate) body: Arc, pub(crate) infer: &'a InferenceResult, -- cgit v1.2.3