diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index fcd22b746769c..846783fd287d1 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -40,6 +40,8 @@ use crate::FnCtxt; use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::Expr; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::relate::RelateResult; use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; @@ -1702,10 +1704,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let Some(expr) = expression { if let hir::ExprKind::Loop( - _, + loop_blk, _, loop_src @ (hir::LoopSource::While | hir::LoopSource::ForLoop), - _, + loop_span, ) = expr.kind { let loop_type = if loop_src == hir::LoopSource::While { @@ -1715,6 +1717,33 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { }; err.note(format!("{loop_type} evaluate to unit type `()`")); + + struct RetExprChecker { + contains_ret_expr: bool, + } + impl<'v> Visitor<'v> for RetExprChecker { + fn visit_expr(&mut self, ex: &'v Expr<'v>) { + if let hir::ExprKind::Ret(_) = ex.kind { + self.contains_ret_expr = true; + return; + } + intravisit::walk_expr(self, ex); + } + } + let mut visitor = RetExprChecker { contains_ret_expr: false }; + intravisit::walk_block(&mut visitor, loop_blk); + if visitor.contains_ret_expr { + let loop_header = if loop_src == hir::LoopSource::While { + "loop condition expression or pattern" + } else { + "iterator expression" + }; + err.span_note( + loop_span, + format!("It is determined that this might iterate zero times, regardless of the {loop_header}.") + ); + err.span_help(loop_span, "If you are assuming that it will iterate at least once, consider using a `loop` expression instead."); + } } fcx.emit_coerce_suggestions( diff --git a/tests/ui/coercion/coerce-loop-issue-122561.stderr b/tests/ui/coercion/coerce-loop-issue-122561.stderr index 0f77fd1364d6b..cc2dc4f0c0c57 100644 --- a/tests/ui/coercion/coerce-loop-issue-122561.stderr +++ b/tests/ui/coercion/coerce-loop-issue-122561.stderr @@ -98,6 +98,16 @@ LL | | } | |_____^ expected `bool`, found `()` | = note: `for` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the iterator expression. + --> $DIR/coerce-loop-issue-122561.rs:4:5 + | +LL | for i in 0.. { + | ^^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/coerce-loop-issue-122561.rs:4:5 + | +LL | for i in 0.. { + | ^^^^^^^^^^^^ help: consider returning a value here | LL ~ } @@ -116,6 +126,16 @@ LL | | } | |_____^ expected `String`, found `()` | = note: `for` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the iterator expression. + --> $DIR/coerce-loop-issue-122561.rs:11:5 + | +LL | for i in 0..5 { + | ^^^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/coerce-loop-issue-122561.rs:11:5 + | +LL | for i in 0..5 { + | ^^^^^^^^^^^^^ help: consider returning a value here | LL ~ } @@ -134,6 +154,16 @@ LL | | } | |_____^ expected `bool`, found `()` | = note: `for` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the iterator expression. + --> $DIR/coerce-loop-issue-122561.rs:18:5 + | +LL | for i in 0..0 { + | ^^^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/coerce-loop-issue-122561.rs:18:5 + | +LL | for i in 0..0 { + | ^^^^^^^^^^^^^ help: consider returning a value here | LL ~ } @@ -168,6 +198,16 @@ LL | fn for_single_line() -> bool { for i in 0.. { return false; } } | expected `bool` because of return type | = note: `for` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the iterator expression. + --> $DIR/coerce-loop-issue-122561.rs:33:32 + | +LL | fn for_single_line() -> bool { for i in 0.. { return false; } } + | ^^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/coerce-loop-issue-122561.rs:33:32 + | +LL | fn for_single_line() -> bool { for i in 0.. { return false; } } + | ^^^^^^^^^^^^ help: consider returning a value here | LL | fn for_single_line() -> bool { for i in 0.. { return false; } /* `bool` value */ } @@ -186,6 +226,16 @@ LL | | } | |_____^ expected `bool`, found `()` | = note: `while` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the loop condition expression or pattern. + --> $DIR/coerce-loop-issue-122561.rs:48:5 + | +LL | while true { + | ^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/coerce-loop-issue-122561.rs:48:5 + | +LL | while true { + | ^^^^^^^^^^ help: consider returning a value here | LL ~ } @@ -206,6 +256,16 @@ LL | | } | |_____^ expected `bool`, found `()` | = note: `while` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the loop condition expression or pattern. + --> $DIR/coerce-loop-issue-122561.rs:57:5 + | +LL | while i < 3 { + | ^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/coerce-loop-issue-122561.rs:57:5 + | +LL | while i < 3 { + | ^^^^^^^^^^^ help: consider returning a value here | LL ~ } @@ -224,6 +284,16 @@ LL | | } | |_____^ expected `bool`, found `()` | = note: `while` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the loop condition expression or pattern. + --> $DIR/coerce-loop-issue-122561.rs:65:5 + | +LL | while false { + | ^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/coerce-loop-issue-122561.rs:65:5 + | +LL | while false { + | ^^^^^^^^^^^ help: consider returning a value here | LL ~ } diff --git a/tests/ui/for-loop-while/break-while-condition.stderr b/tests/ui/for-loop-while/break-while-condition.stderr index 07a57424575fd..abf95a5f2ea9c 100644 --- a/tests/ui/for-loop-while/break-while-condition.stderr +++ b/tests/ui/for-loop-while/break-while-condition.stderr @@ -38,6 +38,16 @@ LL | | } = note: expected type `!` found unit type `()` = note: `while` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the loop condition expression or pattern. + --> $DIR/break-while-condition.rs:24:13 + | +LL | while false { + | ^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/break-while-condition.rs:24:13 + | +LL | while false { + | ^^^^^^^^^^^ help: consider adding a diverging expression here | LL ~ } diff --git a/tests/ui/typeck/irrefutable-while-let-pattern.rs b/tests/ui/typeck/irrefutable-while-let-pattern.rs new file mode 100644 index 0000000000000..6ec81c9ddd01b --- /dev/null +++ b/tests/ui/typeck/irrefutable-while-let-pattern.rs @@ -0,0 +1,8 @@ +fn main() {} + +fn foo() -> bool { + while let x = 0 { + //~^ 4:5: 7:6: mismatched types [E0308] + return true; + } +} diff --git a/tests/ui/typeck/irrefutable-while-let-pattern.stderr b/tests/ui/typeck/irrefutable-while-let-pattern.stderr new file mode 100644 index 0000000000000..8ceba97140e73 --- /dev/null +++ b/tests/ui/typeck/irrefutable-while-let-pattern.stderr @@ -0,0 +1,31 @@ +error[E0308]: mismatched types + --> $DIR/irrefutable-while-let-pattern.rs:4:5 + | +LL | fn foo() -> bool { + | ---- expected `bool` because of return type +LL | / while let x = 0 { +LL | | +LL | | return true; +LL | | } + | |_____^ expected `bool`, found `()` + | + = note: `while` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the loop condition expression or pattern. + --> $DIR/irrefutable-while-let-pattern.rs:4:5 + | +LL | while let x = 0 { + | ^^^^^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/irrefutable-while-let-pattern.rs:4:5 + | +LL | while let x = 0 { + | ^^^^^^^^^^^^^^^ +help: consider returning a value here + | +LL ~ } +LL + /* `bool` value */ + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-100285.rs b/tests/ui/typeck/issue-100285.rs index bea4b2bc2bbe7..aad603c8cae18 100644 --- a/tests/ui/typeck/issue-100285.rs +++ b/tests/ui/typeck/issue-100285.rs @@ -13,8 +13,7 @@ fn foo(n: i32) -> i32 { } else { return 5; } - - } //~ HELP consider returning a value here + } } fn main() {} diff --git a/tests/ui/typeck/issue-100285.stderr b/tests/ui/typeck/issue-100285.stderr index 6f86fd18e0f68..82623fe13e17e 100644 --- a/tests/ui/typeck/issue-100285.stderr +++ b/tests/ui/typeck/issue-100285.stderr @@ -8,15 +8,25 @@ LL | | if n < 0 { LL | | return i; LL | | } else if n < 10 { ... | -LL | | +LL | | } LL | | } | |_____^ expected `i32`, found `()` | = note: `for` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the iterator expression. + --> $DIR/issue-100285.rs:2:5 + | +LL | for i in 0..0 { + | ^^^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/issue-100285.rs:2:5 + | +LL | for i in 0..0 { + | ^^^^^^^^^^^^^ help: consider returning a value here | LL ~ } -LL ~ /* `i32` value */ +LL + /* `i32` value */ | error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-98982.rs b/tests/ui/typeck/issue-98982.rs index 3eff9fbe360b7..715c3fb0c55c0 100644 --- a/tests/ui/typeck/issue-98982.rs +++ b/tests/ui/typeck/issue-98982.rs @@ -1,7 +1,7 @@ fn foo() -> i32 { for i in 0..0 { //~ ERROR mismatched types [E0308] return i; - } //~ HELP consider returning a value here + } } fn main() {} diff --git a/tests/ui/typeck/issue-98982.stderr b/tests/ui/typeck/issue-98982.stderr index 3d40ff4d5afbe..c1ffc40b772fb 100644 --- a/tests/ui/typeck/issue-98982.stderr +++ b/tests/ui/typeck/issue-98982.stderr @@ -9,10 +9,20 @@ LL | | } | |_____^ expected `i32`, found `()` | = note: `for` loops evaluate to unit type `()` +note: It is determined that this might iterate zero times, regardless of the iterator expression. + --> $DIR/issue-98982.rs:2:5 + | +LL | for i in 0..0 { + | ^^^^^^^^^^^^^ +help: If you are assuming that it will iterate at least once, consider using a `loop` expression instead. + --> $DIR/issue-98982.rs:2:5 + | +LL | for i in 0..0 { + | ^^^^^^^^^^^^^ help: consider returning a value here | LL ~ } -LL ~ /* `i32` value */ +LL + /* `i32` value */ | error: aborting due to 1 previous error