From f78a1bd89ade0d326a47222e51c61bd631530416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 15 Jan 2025 22:09:52 +0000 Subject: [PATCH] Detect if-else chains with a missing final else in type errors ``` error[E0308]: `if` and `else` have incompatible types --> $DIR/if-else-chain-missing-else.rs:12:12 | LL | let x = if let Ok(x) = res { | ______________- LL | | x | | - expected because of this LL | | } else if let Err(e) = res { | | ____________^ LL | || return Err(e); LL | || }; | || ^ | ||_____| | |_____`if` and `else` have incompatible types | expected `i32`, found `()` | = note: `if` expressions without `else` evaluate to `()` = note: consider adding an `else` block that evaluates to the expected type ``` We probably want a longer explanation and fewer spans on this case. Partially address #133316. --- .../src/error_reporting/infer/mod.rs | 8 +++++++ .../ui/expr/if/if-else-chain-missing-else.rs | 20 +++++++++++++++++ .../expr/if/if-else-chain-missing-else.stderr | 22 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 tests/ui/expr/if/if-else-chain-missing-else.rs create mode 100644 tests/ui/expr/if/if-else-chain-missing-else.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 7032f7b9d318e..9778299eb191f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -620,6 +620,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) => { let then_span = self.find_block_span_from_hir_id(then_id); let else_span = self.find_block_span_from_hir_id(else_id); + if let hir::Node::Expr(e) = self.tcx.hir_node(else_id) + && let hir::ExprKind::If(_cond, _then, None) = e.kind + && else_ty.is_unit() + { + // Account for `let x = if a { 1 } else if b { 2 };` + err.note("`if` expressions without `else` evaluate to `()`"); + err.note("consider adding an `else` block that evaluates to the expected type"); + } err.span_label(then_span, "expected because of this"); if let Some(sp) = outer_span { err.span_label(sp, "`if` and `else` have incompatible types"); diff --git a/tests/ui/expr/if/if-else-chain-missing-else.rs b/tests/ui/expr/if/if-else-chain-missing-else.rs new file mode 100644 index 0000000000000..995aac07f2f76 --- /dev/null +++ b/tests/ui/expr/if/if-else-chain-missing-else.rs @@ -0,0 +1,20 @@ +enum Cause { Cause1, Cause2 } +struct MyErr { x: Cause } + +fn main() { + _ = f(); +} + +fn f() -> Result { + let res = could_fail(); + let x = if let Ok(x) = res { + x + } else if let Err(e) = res { //~ ERROR `if` and `else` + return Err(e); + }; + Ok(x) +} + +fn could_fail() -> Result { + Ok(0) +} diff --git a/tests/ui/expr/if/if-else-chain-missing-else.stderr b/tests/ui/expr/if/if-else-chain-missing-else.stderr new file mode 100644 index 0000000000000..374c4927e3003 --- /dev/null +++ b/tests/ui/expr/if/if-else-chain-missing-else.stderr @@ -0,0 +1,22 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/if-else-chain-missing-else.rs:12:12 + | +LL | let x = if let Ok(x) = res { + | ______________- +LL | | x + | | - expected because of this +LL | | } else if let Err(e) = res { + | | ____________^ +LL | || return Err(e); +LL | || }; + | || ^ + | ||_____| + | |_____`if` and `else` have incompatible types + | expected `i32`, found `()` + | + = note: `if` expressions without `else` evaluate to `()` + = note: consider adding an `else` block that evaluates to the expected type + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`.