Skip to content

Commit 9a1ee18

Browse files
Merge pull request #20273 from ShoyuVanilla/match-adjusts
fix: Apply adjusts to pats and exprs when doing pat analysis
2 parents 0d4f56d + e587367 commit 9a1ee18

File tree

4 files changed

+94
-48
lines changed

4 files changed

+94
-48
lines changed

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

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,9 @@ impl ExprValidator {
175175
});
176176
}
177177

178-
let receiver_ty = self.infer[*receiver].clone();
179-
checker.prev_receiver_ty = Some(receiver_ty);
178+
if let Some(receiver_ty) = self.infer.type_of_expr_with_adjust(*receiver) {
179+
checker.prev_receiver_ty = Some(receiver_ty.clone());
180+
}
180181
}
181182
}
182183

@@ -187,7 +188,9 @@ impl ExprValidator {
187188
arms: &[MatchArm],
188189
db: &dyn HirDatabase,
189190
) {
190-
let scrut_ty = &self.infer[scrutinee_expr];
191+
let Some(scrut_ty) = self.infer.type_of_expr_with_adjust(scrutinee_expr) else {
192+
return;
193+
};
191194
if scrut_ty.contains_unknown() {
192195
return;
193196
}
@@ -200,7 +203,7 @@ impl ExprValidator {
200203
// Note: Skipping the entire diagnostic rather than just not including a faulty match arm is
201204
// preferred to avoid the chance of false positives.
202205
for arm in arms {
203-
let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) else {
206+
let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else {
204207
return;
205208
};
206209
if pat_ty.contains_unknown() {
@@ -328,7 +331,7 @@ impl ExprValidator {
328331
continue;
329332
}
330333
let Some(initializer) = initializer else { continue };
331-
let ty = &self.infer[initializer];
334+
let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue };
332335
if ty.contains_unknown() {
333336
continue;
334337
}
@@ -433,44 +436,44 @@ impl ExprValidator {
433436
Statement::Expr { expr, .. } => Some(*expr),
434437
_ => None,
435438
});
436-
if let Some(last_then_expr) = last_then_expr {
437-
let last_then_expr_ty = &self.infer[last_then_expr];
438-
if last_then_expr_ty.is_never() {
439-
// Only look at sources if the then branch diverges and we have an else branch.
440-
let source_map = db.body_with_source_map(self.owner).1;
441-
let Ok(source_ptr) = source_map.expr_syntax(id) else {
442-
return;
443-
};
444-
let root = source_ptr.file_syntax(db);
445-
let either::Left(ast::Expr::IfExpr(if_expr)) =
446-
source_ptr.value.to_node(&root)
447-
else {
439+
if let Some(last_then_expr) = last_then_expr
440+
&& let Some(last_then_expr_ty) =
441+
self.infer.type_of_expr_with_adjust(last_then_expr)
442+
&& last_then_expr_ty.is_never()
443+
{
444+
// Only look at sources if the then branch diverges and we have an else branch.
445+
let source_map = db.body_with_source_map(self.owner).1;
446+
let Ok(source_ptr) = source_map.expr_syntax(id) else {
447+
return;
448+
};
449+
let root = source_ptr.file_syntax(db);
450+
let either::Left(ast::Expr::IfExpr(if_expr)) = source_ptr.value.to_node(&root)
451+
else {
452+
return;
453+
};
454+
let mut top_if_expr = if_expr;
455+
loop {
456+
let parent = top_if_expr.syntax().parent();
457+
let has_parent_expr_stmt_or_stmt_list =
458+
parent.as_ref().is_some_and(|node| {
459+
ast::ExprStmt::can_cast(node.kind())
460+
| ast::StmtList::can_cast(node.kind())
461+
});
462+
if has_parent_expr_stmt_or_stmt_list {
463+
// Only emit diagnostic if parent or direct ancestor is either
464+
// an expr stmt or a stmt list.
465+
break;
466+
}
467+
let Some(parent_if_expr) = parent.and_then(ast::IfExpr::cast) else {
468+
// Bail if parent is neither an if expr, an expr stmt nor a stmt list.
448469
return;
449470
};
450-
let mut top_if_expr = if_expr;
451-
loop {
452-
let parent = top_if_expr.syntax().parent();
453-
let has_parent_expr_stmt_or_stmt_list =
454-
parent.as_ref().is_some_and(|node| {
455-
ast::ExprStmt::can_cast(node.kind())
456-
| ast::StmtList::can_cast(node.kind())
457-
});
458-
if has_parent_expr_stmt_or_stmt_list {
459-
// Only emit diagnostic if parent or direct ancestor is either
460-
// an expr stmt or a stmt list.
461-
break;
462-
}
463-
let Some(parent_if_expr) = parent.and_then(ast::IfExpr::cast) else {
464-
// Bail if parent is neither an if expr, an expr stmt nor a stmt list.
465-
return;
466-
};
467-
// Check parent if expr.
468-
top_if_expr = parent_if_expr;
469-
}
470-
471-
self.diagnostics
472-
.push(BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr: id })
471+
// Check parent if expr.
472+
top_if_expr = parent_if_expr;
473473
}
474+
475+
self.diagnostics
476+
.push(BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr: id })
474477
}
475478
}
476479
}

crates/hir-ty/src/infer.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,32 @@ impl InferenceResult {
561561
ExprOrPatId::PatId(id) => self.type_of_pat.get(id),
562562
}
563563
}
564+
pub fn type_of_expr_with_adjust(&self, id: ExprId) -> Option<&Ty> {
565+
match self.expr_adjustments.get(&id).and_then(|adjustments| {
566+
adjustments
567+
.iter()
568+
.filter(|adj| {
569+
// https://github.com/rust-lang/rust/blob/67819923ac8ea353aaa775303f4c3aacbf41d010/compiler/rustc_mir_build/src/thir/cx/expr.rs#L140
570+
!matches!(
571+
adj,
572+
Adjustment {
573+
kind: Adjust::NeverToAny,
574+
target,
575+
} if target.is_never()
576+
)
577+
})
578+
.next_back()
579+
}) {
580+
Some(adjustment) => Some(&adjustment.target),
581+
None => self.type_of_expr.get(id),
582+
}
583+
}
584+
pub fn type_of_pat_with_adjust(&self, id: PatId) -> Option<&Ty> {
585+
match self.pat_adjustments.get(&id).and_then(|adjustments| adjustments.last()) {
586+
adjusted @ Some(_) => adjusted,
587+
None => self.type_of_pat.get(id),
588+
}
589+
}
564590
pub fn is_erroneous(&self) -> bool {
565591
self.has_errors && self.type_of_expr.iter().count() == 0
566592
}

crates/hir/src/source_analyzer.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ impl<'db> SourceAnalyzer<'db> {
441441
) -> Option<GenericSubstitution<'db>> {
442442
let body = self.store()?;
443443
if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] {
444-
let (adt, subst) = type_of_expr_including_adjust(infer, object_expr)?.as_adt()?;
444+
let (adt, subst) = infer.type_of_expr_with_adjust(object_expr)?.as_adt()?;
445445
return Some(GenericSubstitution::new(
446446
adt.into(),
447447
subst.clone(),
@@ -1780,10 +1780,3 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H
17801780
let ctx = span_map.span_at(name.value.text_range().start()).ctx;
17811781
HygieneId::new(ctx.opaque_and_semitransparent(db))
17821782
}
1783-
1784-
fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> {
1785-
match infer.expr_adjustment(id).and_then(|adjustments| adjustments.last()) {
1786-
Some(adjustment) => Some(&adjustment.target),
1787-
None => Some(&infer[id]),
1788-
}
1789-
}

crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,28 @@ fn foo(v: Enum<()>) {
131131
"#,
132132
);
133133
}
134+
135+
#[test]
136+
fn regression_20259() {
137+
check_diagnostics(
138+
r#"
139+
//- minicore: deref
140+
use core::ops::Deref;
141+
142+
struct Foo<T>(T);
143+
144+
impl<T> Deref for Foo<T> {
145+
type Target = T;
146+
147+
fn deref(&self) -> &Self::Target {
148+
&self.0
149+
}
150+
}
151+
152+
fn test(x: Foo<(i32, bool)>) {
153+
let (_a, _b): &(i32, bool) = &x;
154+
}
155+
"#,
156+
);
157+
}
134158
}

0 commit comments

Comments
 (0)