diff options
| author | Lukas Wirth <me@lukaswirth.dev> | 2025-07-06 07:01:40 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-06 07:01:40 +0000 |
| commit | bc10a252847be89f9841d165980921202d92a677 (patch) | |
| tree | 0980911d6731095c6b2c4b140b51d71530475a97 /src/tools | |
| parent | 7fb07c9069b48294419f3fd1448d697eb2cd8d49 (diff) | |
| parent | 4aa30b513f3b12828652fc6a6ae8cbfcc02c1d6b (diff) | |
| download | rust-bc10a252847be89f9841d165980921202d92a677.tar.gz rust-bc10a252847be89f9841d165980921202d92a677.zip | |
Merge pull request #20179 from ChayimFriedman2/destructuring-assignment-never
fix: Fix diverging destructuring assignments
Diffstat (limited to 'src/tools')
| -rw-r--r-- | src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs | 35 | ||||
| -rw-r--r-- | src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs | 28 |
2 files changed, 61 insertions, 2 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index d40d52c134d..d43c99fc282 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -731,9 +731,32 @@ impl InferenceContext<'_> { &Pat::Expr(expr) => { Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No)) } - Pat::Path(path) => Some(self.infer_expr_path(path, target.into(), tgt_expr)), + Pat::Path(path) => { + let resolver_guard = + self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); + let resolution = self.resolver.resolve_path_in_value_ns_fully( + self.db, + path, + self.body.pat_path_hygiene(target), + ); + self.resolver.reset_to_guard(resolver_guard); + + if matches!( + resolution, + Some( + ValueNs::ConstId(_) + | ValueNs::StructId(_) + | ValueNs::EnumVariantId(_) + ) + ) { + None + } else { + Some(self.infer_expr_path(path, target.into(), tgt_expr)) + } + } _ => None, }; + let is_destructuring_assignment = lhs_ty.is_none(); if let Some(lhs_ty) = lhs_ty { self.write_pat_ty(target, lhs_ty.clone()); @@ -747,7 +770,15 @@ impl InferenceContext<'_> { self.inside_assignment = false; self.resolver.reset_to_guard(resolver_guard); } - self.result.standard_types.unit.clone() + if is_destructuring_assignment && self.diverges.is_always() { + // Ordinary assignments always return `()`, even when they diverge. + // However, rustc lowers destructuring assignments into blocks, and blocks return `!` if they have no tail + // expression and they diverge. Therefore, we have to do the same here, even though we don't lower destructuring + // assignments into blocks. + self.table.new_maybe_never_var() + } else { + self.result.standard_types.unit.clone() + } } Expr::Range { lhs, rhs, range_type } => { let lhs_ty = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 1ca4c9b2ad5..6a9135622de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -785,3 +785,31 @@ fn make_up_a_pointer<T>() -> *const T { "#]], ) } + +#[test] +fn diverging_destructuring_assignment() { + check_infer_with_mismatches( + r#" +fn foo() { + let n = match 42 { + 0 => _ = loop {}, + _ => 0, + }; +} + "#, + expect![[r#" + 9..84 '{ ... }; }': () + 19..20 'n': i32 + 23..81 'match ... }': i32 + 29..31 '42': i32 + 42..43 '0': i32 + 42..43 '0': i32 + 47..48 '_': ! + 47..58 '_ = loop {}': i32 + 51..58 'loop {}': ! + 56..58 '{}': () + 68..69 '_': i32 + 73..74 '0': i32 + "#]], + ); +} |
