Unnamed repository; edit this file 'description' to name the repository.
Improve detection of diverging expressions
I.e., consider a block diverging if its tail expr is diverging, even if it doesn't have type never. This became important now that blocks coerce the tail expression's type to the expected type instead of coercing themselves, but in fact the logic was always incorrect - a statement *in the middle of* a block can be diverging without the block having never type. This situation is still not handled correctly, which is why I also left a comment for maybe having a side map for this.
Chayim Refael Friedman 7 days ago
parent 81f51cf · commit e5faf6a
-rw-r--r--crates/hir/src/semantics.rs4
-rw-r--r--crates/hir/src/source_analyzer.rs29
-rw-r--r--crates/ide-assists/src/handlers/convert_match_to_let_else.rs2
-rw-r--r--crates/ide-assists/src/utils.rs10
4 files changed, 35 insertions, 10 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 0d6f9accf8..f633bb063f 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1772,6 +1772,10 @@ impl<'db> SemanticsImpl<'db> {
})
}
+ pub fn expr_is_diverging(&self, expr: &ast::Expr) -> bool {
+ (|| self.analyze(expr.syntax())?.expr_is_diverging(self.db, expr))().unwrap_or(false)
+ }
+
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo<'db>> {
self.analyze(expr.syntax())?
.type_of_expr(self.db, expr)
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 32e6acb6c5..1f9520d780 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -446,6 +446,35 @@ impl<'db> SourceAnalyzer<'db> {
Some(self.ty(ty))
}
+ pub(crate) fn expr_is_diverging(
+ &self,
+ _db: &'db dyn HirDatabase,
+ expr: &ast::Expr,
+ ) -> Option<bool> {
+ let expr_id = self.expr_id(expr.clone())?;
+ let store = self.store()?;
+ let infer = self.infer()?;
+ Some(self.expr_id_is_diverging(store, infer, expr_id))
+ }
+
+ fn expr_id_is_diverging(
+ &self,
+ store: &ExpressionStore,
+ infer: &InferenceResult,
+ expr_id: ExprOrPatId,
+ ) -> bool {
+ // FIXME: This is an approximation, perhaps we need to store a set of diverging exprs in inference?
+ if infer.type_of_expr_or_pat(expr_id).is_some_and(|ty| ty.is_never()) {
+ true
+ } else if let ExprOrPatId::ExprId(expr_id) = expr_id
+ && let Expr::Block { tail: Some(tail), .. } = store[expr_id]
+ {
+ self.expr_id_is_diverging(store, infer, tail.into())
+ } else {
+ false
+ }
+ }
+
pub(crate) fn type_of_expr(
&self,
_db: &'db dyn HirDatabase,
diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index a93ab138e5..9dffdf3f36 100644
--- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -93,7 +93,7 @@ fn find_arms(
let mut extracting = None;
let mut diverging = None;
for arm in arms {
- if ctx.sema.type_of_expr(&arm.expr()?)?.original().is_never() {
+ if ctx.sema.expr_is_diverging(&arm.expr()?) {
diverging = Some(arm);
} else {
extracting = Some(arm);
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 33e0f476da..1b6c9a579a 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -1182,13 +1182,5 @@ pub(crate) fn is_never_block(
sema: &Semantics<'_, RootDatabase>,
block_expr: &ast::BlockExpr,
) -> bool {
- if let Some(tail_expr) = block_expr.tail_expr() {
- sema.type_of_expr(&tail_expr).is_some_and(|ty| ty.original.is_never())
- } else if let Some(ast::Stmt::ExprStmt(expr_stmt)) = block_expr.statements().last()
- && let Some(expr) = expr_stmt.expr()
- {
- sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never())
- } else {
- false
- }
+ sema.expr_is_diverging(&block_expr.clone().into())
}