Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #20273 from ShoyuVanilla/match-adjusts
fix: Apply adjusts to pats and exprs when doing pat analysis
Chayim Refael Friedman 9 months ago
parent 0d4f56d · parent e587367 · commit 9a1ee18
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs83
-rw-r--r--crates/hir-ty/src/infer.rs26
-rw-r--r--crates/hir/src/source_analyzer.rs9
-rw-r--r--crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs24
4 files changed, 94 insertions, 48 deletions
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 5ae6bf6dff..cc531f076d 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -175,8 +175,9 @@ impl ExprValidator {
});
}
- let receiver_ty = self.infer[*receiver].clone();
- checker.prev_receiver_ty = Some(receiver_ty);
+ if let Some(receiver_ty) = self.infer.type_of_expr_with_adjust(*receiver) {
+ checker.prev_receiver_ty = Some(receiver_ty.clone());
+ }
}
}
@@ -187,7 +188,9 @@ impl ExprValidator {
arms: &[MatchArm],
db: &dyn HirDatabase,
) {
- let scrut_ty = &self.infer[scrutinee_expr];
+ let Some(scrut_ty) = self.infer.type_of_expr_with_adjust(scrutinee_expr) else {
+ return;
+ };
if scrut_ty.contains_unknown() {
return;
}
@@ -200,7 +203,7 @@ impl ExprValidator {
// Note: Skipping the entire diagnostic rather than just not including a faulty match arm is
// preferred to avoid the chance of false positives.
for arm in arms {
- let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) else {
+ let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else {
return;
};
if pat_ty.contains_unknown() {
@@ -328,7 +331,7 @@ impl ExprValidator {
continue;
}
let Some(initializer) = initializer else { continue };
- let ty = &self.infer[initializer];
+ let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue };
if ty.contains_unknown() {
continue;
}
@@ -433,44 +436,44 @@ impl ExprValidator {
Statement::Expr { expr, .. } => Some(*expr),
_ => None,
});
- if let Some(last_then_expr) = last_then_expr {
- let last_then_expr_ty = &self.infer[last_then_expr];
- if last_then_expr_ty.is_never() {
- // Only look at sources if the then branch diverges and we have an else branch.
- let source_map = db.body_with_source_map(self.owner).1;
- let Ok(source_ptr) = source_map.expr_syntax(id) else {
- return;
- };
- let root = source_ptr.file_syntax(db);
- let either::Left(ast::Expr::IfExpr(if_expr)) =
- source_ptr.value.to_node(&root)
- else {
+ if let Some(last_then_expr) = last_then_expr
+ && let Some(last_then_expr_ty) =
+ self.infer.type_of_expr_with_adjust(last_then_expr)
+ && last_then_expr_ty.is_never()
+ {
+ // Only look at sources if the then branch diverges and we have an else branch.
+ let source_map = db.body_with_source_map(self.owner).1;
+ let Ok(source_ptr) = source_map.expr_syntax(id) else {
+ return;
+ };
+ let root = source_ptr.file_syntax(db);
+ let either::Left(ast::Expr::IfExpr(if_expr)) = source_ptr.value.to_node(&root)
+ else {
+ return;
+ };
+ let mut top_if_expr = if_expr;
+ loop {
+ let parent = top_if_expr.syntax().parent();
+ let has_parent_expr_stmt_or_stmt_list =
+ parent.as_ref().is_some_and(|node| {
+ ast::ExprStmt::can_cast(node.kind())
+ | ast::StmtList::can_cast(node.kind())
+ });
+ if has_parent_expr_stmt_or_stmt_list {
+ // Only emit diagnostic if parent or direct ancestor is either
+ // an expr stmt or a stmt list.
+ break;
+ }
+ let Some(parent_if_expr) = parent.and_then(ast::IfExpr::cast) else {
+ // Bail if parent is neither an if expr, an expr stmt nor a stmt list.
return;
};
- let mut top_if_expr = if_expr;
- loop {
- let parent = top_if_expr.syntax().parent();
- let has_parent_expr_stmt_or_stmt_list =
- parent.as_ref().is_some_and(|node| {
- ast::ExprStmt::can_cast(node.kind())
- | ast::StmtList::can_cast(node.kind())
- });
- if has_parent_expr_stmt_or_stmt_list {
- // Only emit diagnostic if parent or direct ancestor is either
- // an expr stmt or a stmt list.
- break;
- }
- let Some(parent_if_expr) = parent.and_then(ast::IfExpr::cast) else {
- // Bail if parent is neither an if expr, an expr stmt nor a stmt list.
- return;
- };
- // Check parent if expr.
- top_if_expr = parent_if_expr;
- }
-
- self.diagnostics
- .push(BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr: id })
+ // Check parent if expr.
+ top_if_expr = parent_if_expr;
}
+
+ self.diagnostics
+ .push(BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr: id })
}
}
}
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index e880438e3a..7c39afa0ef 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -561,6 +561,32 @@ impl InferenceResult {
ExprOrPatId::PatId(id) => self.type_of_pat.get(id),
}
}
+ pub fn type_of_expr_with_adjust(&self, id: ExprId) -> Option<&Ty> {
+ match self.expr_adjustments.get(&id).and_then(|adjustments| {
+ adjustments
+ .iter()
+ .filter(|adj| {
+ // https://github.com/rust-lang/rust/blob/67819923ac8ea353aaa775303f4c3aacbf41d010/compiler/rustc_mir_build/src/thir/cx/expr.rs#L140
+ !matches!(
+ adj,
+ Adjustment {
+ kind: Adjust::NeverToAny,
+ target,
+ } if target.is_never()
+ )
+ })
+ .next_back()
+ }) {
+ Some(adjustment) => Some(&adjustment.target),
+ None => self.type_of_expr.get(id),
+ }
+ }
+ pub fn type_of_pat_with_adjust(&self, id: PatId) -> Option<&Ty> {
+ match self.pat_adjustments.get(&id).and_then(|adjustments| adjustments.last()) {
+ adjusted @ Some(_) => adjusted,
+ None => self.type_of_pat.get(id),
+ }
+ }
pub fn is_erroneous(&self) -> bool {
self.has_errors && self.type_of_expr.iter().count() == 0
}
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index ecc6e5f3d0..0b554a9d4e 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -441,7 +441,7 @@ impl<'db> SourceAnalyzer<'db> {
) -> Option<GenericSubstitution<'db>> {
let body = self.store()?;
if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] {
- let (adt, subst) = type_of_expr_including_adjust(infer, object_expr)?.as_adt()?;
+ let (adt, subst) = infer.type_of_expr_with_adjust(object_expr)?.as_adt()?;
return Some(GenericSubstitution::new(
adt.into(),
subst.clone(),
@@ -1780,10 +1780,3 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H
let ctx = span_map.span_at(name.value.text_range().start()).ctx;
HygieneId::new(ctx.opaque_and_semitransparent(db))
}
-
-fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> {
- match infer.expr_adjustment(id).and_then(|adjustments| adjustments.last()) {
- Some(adjustment) => Some(&adjustment.target),
- None => Some(&infer[id]),
- }
-}
diff --git a/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
index f20b6dea12..e31367f3b1 100644
--- a/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
+++ b/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
@@ -131,4 +131,28 @@ fn foo(v: Enum<()>) {
"#,
);
}
+
+ #[test]
+ fn regression_20259() {
+ check_diagnostics(
+ r#"
+//- minicore: deref
+use core::ops::Deref;
+
+struct Foo<T>(T);
+
+impl<T> Deref for Foo<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+fn test(x: Foo<(i32, bool)>) {
+ let (_a, _b): &(i32, bool) = &x;
+}
+"#,
+ );
+ }
}