diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index fdf448f5e11..87a85797613 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs @@ -11,8 +11,11 @@ pub(crate) mod usefulness; use hir_def::{body::Body, expr::PatId, EnumVariantId, LocalFieldId, VariantId}; +use stdx::never; -use crate::{db::HirDatabase, InferenceResult, Interner, Substitution, Ty, TyKind}; +use crate::{ + db::HirDatabase, infer::BindingMode, InferenceResult, Interner, Substitution, Ty, TyKind, +}; use self::pat_util::EnumerateAndAdjustIterator; @@ -21,6 +24,7 @@ #[derive(Clone, Debug)] pub(crate) enum PatternError { Unimplemented, + UnexpectedType, UnresolvedVariant, MissingField, ExtraFields, @@ -129,9 +133,16 @@ fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat { PatKind::Leaf { subpatterns } } - hir_def::expr::Pat::Bind { subpat, .. } => { - if let TyKind::Ref(.., rty) = ty.kind(Interner) { - ty = rty; + hir_def::expr::Pat::Bind { ref name, subpat, .. } => { + let bm = self.infer.pat_binding_modes[&pat]; + match (bm, ty.kind(Interner)) { + (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty, + (BindingMode::Ref(_), _) => { + never!("`ref {}` has wrong type {:?}", name, ty); + self.errors.push(PatternError::UnexpectedType); + return Pat { ty: ty.clone(), kind: PatKind::Wild.into() }; + } + _ => (), } PatKind::Binding { subpattern: self.lower_opt_pattern(subpat) } } diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 95d9a6735a3..54c3590f04b 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -100,7 +100,7 @@ enum ExprOrPatId { /// Binding modes inferred for patterns. /// #[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum BindingMode { +pub enum BindingMode { Move, Ref(Mutability), } @@ -292,6 +292,7 @@ pub struct InferenceResult { standard_types: InternedStandardTypes, /// Stores the types which were implicitly dereferenced in pattern binding modes. pub pat_adjustments: FxHashMap>, + pub pat_binding_modes: FxHashMap, pub expr_adjustments: FxHashMap>, } diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index 64e72abf0f3..50fd2dd7494 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs @@ -204,6 +204,8 @@ pub(super) fn infer_pat( } else { BindingMode::convert(*mode) }; + self.result.pat_binding_modes.insert(pat, mode); + let inner_ty = match subpat { Some(subpat) => self.infer_pat(*subpat, &expected, default_bm), None => expected, diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs index 5ef4d2a2266..6e2764e59ff 100644 --- a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs @@ -883,7 +883,20 @@ enum E {Foo, Bar} match &n { Next(E::Foo | E::Bar) => {} } match &n { _ | Next(E::Bar) => {} } };", + ); + } + #[test] + fn binding_mode_by_ref() { + check_diagnostics_no_bails( + r" +enum E{ A, B } +fn foo() { + match &E::A { + E::A => {} + x => {} + } +}", ); }