Repository: rust-lang/rust-analyzer
Pull request #12355 (2022-05-23 10:30:19)

Fix inference when pattern matching a tuple field with a wildcard
This should fix the following issue:  https://github.com/rust-lang/rust-analyzer/issues/12331

* Replaced the `err_ty` in `infer_pat()` with a new type variable.
* Had to change the iterator code a bit, to get around multiple mutable borrows of `self` in `infer_pat()`.
Also added a test 
* Also added a test


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index f3bd3d6b76..8d2217b2e2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -982,9 +982,9 @@ dependencies = [
 
 [[package]]
 name = "notify"
-version = "5.0.0-pre.15"
+version = "5.0.0-pre.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "553f9844ad0b0824605c20fb55a661679782680410abfb1a8144c2e7e437e7a7"
+checksum = "d13c22db70a63592e098fb51735bab36646821e6389a0ba171f3549facdf0b74"
 dependencies = [
  "bitflags",
  "crossbeam-channel",
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index f8131314c6..dbce0f7cd4 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -1,6 +1,6 @@
 //! Type inference for patterns.
 
-use std::iter::repeat;
+use std::iter::repeat_with;
 
 use chalk_ir::Mutability;
 use hir_def::{
@@ -140,15 +140,28 @@ impl<'a> InferenceContext<'a> {
                     }
                     None => ((&args[..], &[][..]), 0),
                 };
-                let err_ty = self.err_ty();
-                let mut expectations_iter =
-                    expectations.iter().map(|a| a.assert_ty_ref(Interner)).chain(repeat(&err_ty));
-                let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm);
+                let mut expectations_iter = expectations
+                    .iter()
+                    .cloned()
+                    .map(|a| a.assert_ty_ref(Interner).clone())
+                    .chain(repeat_with(|| self.table.new_type_var()));
 
                 let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len());
-                inner_tys.extend(pre.iter().zip(expectations_iter.by_ref()).map(&mut infer_pat));
-                inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned());
-                inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat));
+
+                inner_tys
+                    .extend(expectations_iter.by_ref().take(n_uncovered_patterns + args.len()));
+
+                // Process pre
+                for (ty, pat) in inner_tys.iter_mut().zip(pre) {
+                    *ty = self.infer_pat(*pat, ty, default_bm);
+                }
+
+                // Process post
+                for (ty, pat) in
+                    inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post)
+                {
+                    *ty = self.infer_pat(*pat, ty, default_bm);
+                }
 
                 TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
                     .intern(Interner)
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index efa9afcbb4..d3973fa7ab 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -7,7 +7,7 @@
 //! not instantaneous -- it takes time to run `cargo metadata` and (for proc
 //! macros) `cargo check`.
 //!
-//! The main guiding principle here is, as elsewhere in rust-analyzer,
+//! The main guiding principle here is, as elsewhere in rust-analyzer, is
 //! robustness. We try not to assume that the project model exists or is
 //! correct. Instead, we try to provide a best-effort service. Even if the
 //! project is currently loading and we don't have a full project model, we
diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml
index d9ac83af59..a13a85f92f 100644
--- a/crates/vfs-notify/Cargo.toml
+++ b/crates/vfs-notify/Cargo.toml
@@ -14,7 +14,7 @@ tracing = "0.1.32"
 jod-thread = "0.1.2"
 walkdir = "2.3.2"
 crossbeam-channel = "0.5.4"
-notify = "=5.0.0-pre.15"
+notify = "=5.0.0-pre.14"
 
 vfs = { path = "../vfs", version = "0.0.0" }
 paths = { path = "../paths", version = "0.0.0" }


Test patch:
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index acdd8f50ef..399553356b 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -969,3 +969,23 @@ fn main() {
     "#,
     );
 }
+
+#[test]
+fn tuple_wildcard() {
+    check_types(
+        r#"
+fn main() {
+    enum Option<T> {Some(T), None}
+    use Option::*;
+
+    let mut x = None;
+    x;
+  //^ Option<(i32, i32)>
+
+    if let Some((_, _a)) = x {}
+
+    x = Some((1, 2));
+}
+        "#,
+    );
+}