Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 884835e

Browse files
committed
Auto merge of rust-lang#14732 - HKalbasi:pat-type-mismatch, r=HKalbasi
Fix pattern type mismatch in tuples Reduces pattern type mismatches on self to 4
2 parents 60f4b3e + 36c9d5c commit 884835e

File tree

13 files changed

+139
-45
lines changed

13 files changed

+139
-45
lines changed

crates/hir-ty/src/diagnostics/match_check.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ impl<'a> PatCtxt<'a> {
148148

149149
hir_def::hir::Pat::Bind { id, subpat, .. } => {
150150
let bm = self.infer.pat_binding_modes[&pat];
151+
ty = &self.infer[id];
151152
let name = &self.body.bindings[id].name;
152153
match (bm, ty.kind(Interner)) {
153154
(BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,

crates/hir-ty/src/infer/pat.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ impl<'a> InferenceContext<'a> {
263263
// Don't emit type mismatches again, the expression lowering already did that.
264264
let ty = self.infer_lit_pat(expr, &expected);
265265
self.write_pat_ty(pat, ty.clone());
266-
return ty;
266+
return self.pat_ty_after_adjustment(pat);
267267
}
268268
Pat::Box { inner } => match self.resolve_boxed_box() {
269269
Some(box_adt) => {
@@ -298,8 +298,17 @@ impl<'a> InferenceContext<'a> {
298298
.type_mismatches
299299
.insert(pat.into(), TypeMismatch { expected, actual: ty.clone() });
300300
}
301-
self.write_pat_ty(pat, ty.clone());
302-
ty
301+
self.write_pat_ty(pat, ty);
302+
self.pat_ty_after_adjustment(pat)
303+
}
304+
305+
fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty {
306+
self.result
307+
.pat_adjustments
308+
.get(&pat)
309+
.and_then(|x| x.first())
310+
.unwrap_or(&self.result.type_of_pat[pat])
311+
.clone()
303312
}
304313

305314
fn infer_ref_pat(
@@ -345,7 +354,7 @@ impl<'a> InferenceContext<'a> {
345354
}
346355
BindingMode::Move => inner_ty.clone(),
347356
};
348-
self.write_pat_ty(pat, bound_ty.clone());
357+
self.write_pat_ty(pat, inner_ty.clone());
349358
self.write_binding_ty(binding, bound_ty);
350359
return inner_ty;
351360
}
@@ -422,14 +431,6 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
422431
Pat::Lit(expr) => {
423432
!matches!(body[*expr], Expr::Literal(Literal::String(..) | Literal::ByteString(..)))
424433
}
425-
Pat::Bind { id, subpat: Some(subpat), .. }
426-
if matches!(
427-
body.bindings[*id].mode,
428-
BindingAnnotation::Mutable | BindingAnnotation::Unannotated
429-
) =>
430-
{
431-
is_non_ref_pat(body, *subpat)
432-
}
433434
Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
434435
}
435436
}

crates/hir-ty/src/tests.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use expect_test::Expect;
1717
use hir_def::{
1818
body::{Body, BodySourceMap, SyntheticSyntax},
1919
db::{DefDatabase, InternDatabase},
20-
hir::{ExprId, PatId},
20+
hir::{ExprId, Pat, PatId},
2121
item_scope::ItemScope,
2222
nameres::DefMap,
2323
src::HasSource,
@@ -149,10 +149,13 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
149149
});
150150
let mut unexpected_type_mismatches = String::new();
151151
for def in defs {
152-
let (_body, body_source_map) = db.body_with_source_map(def);
152+
let (body, body_source_map) = db.body_with_source_map(def);
153153
let inference_result = db.infer(def);
154154

155-
for (pat, ty) in inference_result.type_of_pat.iter() {
155+
for (pat, mut ty) in inference_result.type_of_pat.iter() {
156+
if let Pat::Bind { id, .. } = body.pats[pat] {
157+
ty = &inference_result.type_of_binding[id];
158+
}
156159
let node = match pat_node(&body_source_map, pat, &db) {
157160
Some(value) => value,
158161
None => continue,
@@ -284,11 +287,15 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
284287
let mut buf = String::new();
285288

286289
let mut infer_def = |inference_result: Arc<InferenceResult>,
290+
body: Arc<Body>,
287291
body_source_map: Arc<BodySourceMap>| {
288292
let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
289293
let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
290294

291-
for (pat, ty) in inference_result.type_of_pat.iter() {
295+
for (pat, mut ty) in inference_result.type_of_pat.iter() {
296+
if let Pat::Bind { id, .. } = body.pats[pat] {
297+
ty = &inference_result.type_of_binding[id];
298+
}
292299
let syntax_ptr = match body_source_map.pat_syntax(pat) {
293300
Ok(sp) => {
294301
let root = db.parse_or_expand(sp.file_id);
@@ -386,9 +393,9 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
386393
}
387394
});
388395
for def in defs {
389-
let (_body, source_map) = db.body_with_source_map(def);
396+
let (body, source_map) = db.body_with_source_map(def);
390397
let infer = db.infer(def);
391-
infer_def(infer, source_map);
398+
infer_def(infer, body, source_map);
392399
}
393400

394401
buf.truncate(buf.trim_end().len());

crates/hir-ty/src/tests/simple.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,6 +2033,56 @@ fn test() {
20332033
);
20342034
}
20352035

2036+
#[test]
2037+
fn tuple_pattern_nested_match_ergonomics() {
2038+
check_no_mismatches(
2039+
r#"
2040+
fn f(x: (&i32, &i32)) -> i32 {
2041+
match x {
2042+
(3, 4) => 5,
2043+
_ => 12,
2044+
}
2045+
}
2046+
"#,
2047+
);
2048+
check_types(
2049+
r#"
2050+
fn f(x: (&&&&i32, &&&i32)) {
2051+
let f = match x {
2052+
t @ (3, 4) => t,
2053+
_ => loop {},
2054+
};
2055+
f;
2056+
//^ (&&&&i32, &&&i32)
2057+
}
2058+
"#,
2059+
);
2060+
check_types(
2061+
r#"
2062+
fn f() {
2063+
let x = &&&(&&&2, &&&&&3);
2064+
let (y, z) = x;
2065+
//^ &&&&i32
2066+
let t @ (y, z) = x;
2067+
t;
2068+
//^ &&&(&&&i32, &&&&&i32)
2069+
}
2070+
"#,
2071+
);
2072+
check_types(
2073+
r#"
2074+
fn f() {
2075+
let x = &&&(&&&2, &&&&&3);
2076+
let (y, z) = x;
2077+
//^ &&&&i32
2078+
let t @ (y, z) = x;
2079+
t;
2080+
//^ &&&(&&&i32, &&&&&i32)
2081+
}
2082+
"#,
2083+
);
2084+
}
2085+
20362086
#[test]
20372087
fn fn_pointer_return() {
20382088
check_infer(

crates/hir/src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,9 +1535,6 @@ impl DefWithBody {
15351535
for (pat_or_expr, mismatch) in infer.type_mismatches() {
15361536
let expr_or_pat = match pat_or_expr {
15371537
ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left),
1538-
// FIXME: Re-enable these once we have less false positives
1539-
ExprOrPatId::PatId(_pat) => continue,
1540-
#[allow(unreachable_patterns)]
15411538
ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
15421539
};
15431540
let expr_or_pat = match expr_or_pat {

crates/hir/src/semantics.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
350350
self.imp.type_of_pat(pat)
351351
}
352352

353+
/// It also includes the changes that binding mode makes in the type. For example in
354+
/// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result
355+
/// of this function is `&mut Option<T>`
356+
pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
357+
self.imp.type_of_binding_in_pat(pat)
358+
}
359+
353360
pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
354361
self.imp.type_of_self(param)
355362
}
@@ -1138,6 +1145,10 @@ impl<'db> SemanticsImpl<'db> {
11381145
.map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
11391146
}
11401147

1148+
fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
1149+
self.analyze(pat.syntax())?.type_of_binding_in_pat(self.db, pat)
1150+
}
1151+
11411152
fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
11421153
self.analyze(param.syntax())?.type_of_self(self.db, param)
11431154
}

crates/hir/src/source_analyzer.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use hir_def::{
1313
scope::{ExprScopes, ScopeId},
1414
Body, BodySourceMap,
1515
},
16-
hir::{ExprId, Pat, PatId},
16+
hir::{BindingId, ExprId, Pat, PatId},
1717
lang_item::LangItem,
1818
lower::LowerCtx,
1919
macro_id_to_def_id,
@@ -133,6 +133,15 @@ impl SourceAnalyzer {
133133
self.body_source_map()?.node_pat(src)
134134
}
135135

136+
fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingId> {
137+
let pat_id = self.pat_id(&pat.clone().into())?;
138+
if let Pat::Bind { id, .. } = self.body()?.pats[pat_id] {
139+
Some(id)
140+
} else {
141+
None
142+
}
143+
}
144+
136145
fn expand_expr(
137146
&self,
138147
db: &dyn HirDatabase,
@@ -198,6 +207,18 @@ impl SourceAnalyzer {
198207
Some((mk_ty(ty), coerced.map(mk_ty)))
199208
}
200209

210+
pub(crate) fn type_of_binding_in_pat(
211+
&self,
212+
db: &dyn HirDatabase,
213+
pat: &ast::IdentPat,
214+
) -> Option<Type> {
215+
let binding_id = self.binding_id_of_pat(pat)?;
216+
let infer = self.infer.as_ref()?;
217+
let ty = infer[binding_id].clone();
218+
let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
219+
Some(mk_ty(ty))
220+
}
221+
201222
pub(crate) fn type_of_self(
202223
&self,
203224
db: &dyn HirDatabase,

crates/ide-assists/src/handlers/destructure_tuple_binding.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat
9191
return None;
9292
}
9393

94-
let ty = ctx.sema.type_of_pat(&ident_pat.clone().into())?.adjusted();
94+
let ty = ctx.sema.type_of_binding_in_pat(&ident_pat)?;
9595
let ref_type = if ty.is_mutable_reference() {
9696
Some(RefType::Mutable)
9797
} else if ty.is_reference() {

crates/ide-assists/src/handlers/merge_match_arms.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use hir::TypeInfo;
1+
use hir::Type;
22
use std::{collections::HashMap, iter::successors};
33
use syntax::{
44
algo::neighbor,
@@ -95,15 +95,15 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool {
9595
}
9696

9797
fn are_same_types(
98-
current_arm_types: &HashMap<String, Option<TypeInfo>>,
98+
current_arm_types: &HashMap<String, Option<Type>>,
9999
arm: &ast::MatchArm,
100100
ctx: &AssistContext<'_>,
101101
) -> bool {
102102
let arm_types = get_arm_types(ctx, arm);
103103
for (other_arm_type_name, other_arm_type) in arm_types {
104104
match (current_arm_types.get(&other_arm_type_name), other_arm_type) {
105105
(Some(Some(current_arm_type)), Some(other_arm_type))
106-
if other_arm_type.original == current_arm_type.original => {}
106+
if other_arm_type == *current_arm_type => {}
107107
_ => return false,
108108
}
109109
}
@@ -114,44 +114,44 @@ fn are_same_types(
114114
fn get_arm_types(
115115
context: &AssistContext<'_>,
116116
arm: &ast::MatchArm,
117-
) -> HashMap<String, Option<TypeInfo>> {
118-
let mut mapping: HashMap<String, Option<TypeInfo>> = HashMap::new();
117+
) -> HashMap<String, Option<Type>> {
118+
let mut mapping: HashMap<String, Option<Type>> = HashMap::new();
119119

120120
fn recurse(
121-
map: &mut HashMap<String, Option<TypeInfo>>,
121+
map: &mut HashMap<String, Option<Type>>,
122122
ctx: &AssistContext<'_>,
123123
pat: &Option<ast::Pat>,
124124
) {
125125
if let Some(local_pat) = pat {
126-
match pat {
127-
Some(ast::Pat::TupleStructPat(tuple)) => {
126+
match local_pat {
127+
ast::Pat::TupleStructPat(tuple) => {
128128
for field in tuple.fields() {
129129
recurse(map, ctx, &Some(field));
130130
}
131131
}
132-
Some(ast::Pat::TuplePat(tuple)) => {
132+
ast::Pat::TuplePat(tuple) => {
133133
for field in tuple.fields() {
134134
recurse(map, ctx, &Some(field));
135135
}
136136
}
137-
Some(ast::Pat::RecordPat(record)) => {
137+
ast::Pat::RecordPat(record) => {
138138
if let Some(field_list) = record.record_pat_field_list() {
139139
for field in field_list.fields() {
140140
recurse(map, ctx, &field.pat());
141141
}
142142
}
143143
}
144-
Some(ast::Pat::ParenPat(parentheses)) => {
144+
ast::Pat::ParenPat(parentheses) => {
145145
recurse(map, ctx, &parentheses.pat());
146146
}
147-
Some(ast::Pat::SlicePat(slice)) => {
147+
ast::Pat::SlicePat(slice) => {
148148
for slice_pat in slice.pats() {
149149
recurse(map, ctx, &Some(slice_pat));
150150
}
151151
}
152-
Some(ast::Pat::IdentPat(ident_pat)) => {
152+
ast::Pat::IdentPat(ident_pat) => {
153153
if let Some(name) = ident_pat.name() {
154-
let pat_type = ctx.sema.type_of_pat(local_pat);
154+
let pat_type = ctx.sema.type_of_binding_in_pat(ident_pat);
155155
map.insert(name.text().to_string(), pat_type);
156156
}
157157
}

0 commit comments

Comments
 (0)