diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 9ebb5f95f05fd..5a70890d652d2 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -37,11 +37,9 @@ use crate::errors::SuggestBoxingForReturnImplTrait; use crate::FnCtxt; -use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan}; +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::type_variable::TypeVariableOrigin; use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; @@ -95,22 +93,6 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { type CoerceResult<'tcx> = InferResult<'tcx, (Vec>, Ty<'tcx>)>; -struct CollectRetsVisitor<'tcx> { - ret_exprs: Vec<&'tcx hir::Expr<'tcx>>, -} - -impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match expr.kind { - hir::ExprKind::Ret(_) => self.ret_exprs.push(expr), - // `return` in closures does not return from the outer function - hir::ExprKind::Closure(_) => return, - _ => {} - } - intravisit::walk_expr(self, expr); - } -} - /// Coercing a mutable reference to an immutable works, while /// coercing `&T` to `&mut T` should be forbidden. fn coerce_mutbls<'tcx>( @@ -1601,7 +1583,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let mut err; let mut unsized_return = false; - let mut visitor = CollectRetsVisitor { ret_exprs: vec![] }; match *cause.code() { ObligationCauseCode::ReturnNoExpression => { err = struct_span_code_err!( @@ -1636,11 +1617,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_definitely_unsized(fcx); } - if let Some(expression) = expression - && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind - { - intravisit::walk_block(&mut visitor, loop_blk); - } } ObligationCauseCode::ReturnValue(id) => { err = self.report_return_mismatched_types( @@ -1741,6 +1717,22 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { augment_error(&mut err); if let Some(expr) = expression { + if let hir::ExprKind::Loop( + _, + _, + loop_src @ (hir::LoopSource::While | hir::LoopSource::ForLoop), + _, + ) = expr.kind + { + let loop_type = if loop_src == hir::LoopSource::While { + "`while` loops" + } else { + "`for` loops" + }; + + err.note(format!("{loop_type} evaluate to unit type `()`")); + } + fcx.emit_coerce_suggestions( &mut err, expr, @@ -1749,15 +1741,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { None, Some(coercion_error), ); - if visitor.ret_exprs.len() > 0 { - self.note_unreachable_loop_return( - &mut err, - fcx.tcx, - &expr, - &visitor.ret_exprs, - expected, - ); - } } let reported = err.emit_unless(unsized_return); @@ -1831,110 +1814,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ); } - fn note_unreachable_loop_return( - &self, - err: &mut Diag<'_>, - tcx: TyCtxt<'tcx>, - expr: &hir::Expr<'tcx>, - ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>, - ty: Ty<'tcx>, - ) { - let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { - return; - }; - let mut span: MultiSpan = vec![loop_span].into(); - span.push_span_label(loop_span, "this might have zero elements to iterate on"); - const MAXITER: usize = 3; - let iter = ret_exprs.iter().take(MAXITER); - for ret_expr in iter { - span.push_span_label( - ret_expr.span, - "if the loop doesn't execute, this value would never get returned", - ); - } - err.span_note( - span, - "the function expects a value to always be returned, but loops might run zero times", - ); - if MAXITER < ret_exprs.len() { - err.note(format!( - "if the loop doesn't execute, {} other values would never get returned", - ret_exprs.len() - MAXITER - )); - } - let hir = tcx.hir(); - let item = hir.get_parent_item(expr.hir_id); - let ret_msg = "return a value for the case when the loop has zero elements to iterate on"; - let ret_ty_msg = - "otherwise consider changing the return type to account for that possibility"; - let node = tcx.hir_node(item.into()); - if let Some(body_id) = node.body_id() - && let Some(sig) = node.fn_sig() - && let hir::ExprKind::Block(block, _) = hir.body(body_id).value.kind - && !ty.is_never() - { - let indentation = if let None = block.expr - && let [.., last] = &block.stmts - { - tcx.sess.source_map().indentation_before(last.span).unwrap_or_else(String::new) - } else if let Some(expr) = block.expr { - tcx.sess.source_map().indentation_before(expr.span).unwrap_or_else(String::new) - } else { - String::new() - }; - if let None = block.expr - && let [.., last] = &block.stmts - { - err.span_suggestion_verbose( - last.span.shrink_to_hi(), - ret_msg, - format!("\n{indentation}/* `{ty}` value */"), - Applicability::MaybeIncorrect, - ); - } else if let Some(expr) = block.expr { - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - ret_msg, - format!("\n{indentation}/* `{ty}` value */"), - Applicability::MaybeIncorrect, - ); - } - let mut sugg = match sig.decl.output { - hir::FnRetTy::DefaultReturn(span) => { - vec![(span, " -> Option<()>".to_string())] - } - hir::FnRetTy::Return(ty) => { - vec![ - (ty.span.shrink_to_lo(), "Option<".to_string()), - (ty.span.shrink_to_hi(), ">".to_string()), - ] - } - }; - for ret_expr in ret_exprs { - match ret_expr.kind { - hir::ExprKind::Ret(Some(expr)) => { - sugg.push((expr.span.shrink_to_lo(), "Some(".to_string())); - sugg.push((expr.span.shrink_to_hi(), ")".to_string())); - } - hir::ExprKind::Ret(None) => { - sugg.push((ret_expr.span.shrink_to_hi(), " Some(())".to_string())); - } - _ => {} - } - } - if let None = block.expr - && let [.., last] = &block.stmts - { - sugg.push((last.span.shrink_to_hi(), format!("\n{indentation}None"))); - } else if let Some(expr) = block.expr { - sugg.push((expr.span.shrink_to_hi(), format!("\n{indentation}None"))); - } - err.multipart_suggestion(ret_ty_msg, sugg, Applicability::MaybeIncorrect); - } else { - err.help(format!("{ret_msg}, {ret_ty_msg}")); - } - } - fn report_return_mismatched_types<'a>( &self, cause: &ObligationCause<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d6d22a43fe0b8..0a05c14f389ac 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -57,7 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_into(err, expr, expr_ty, expected) || self.suggest_floating_point_literal(err, expr, expected) || self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected) - || self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty); + || self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty) + || self.suggest_returning_value_after_loop(err, expr, expected); if !suggested { self.note_source_of_type_mismatch_constraint( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 43e5ab0ed53f8..3d476abb197b2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -18,8 +18,8 @@ use rustc_hir::def::Res; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node, - Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, + Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, + Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -1942,6 +1942,91 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + // If the expr is a while or for loop and is the tail expr of its + // enclosing body suggest returning a value right after it + pub fn suggest_returning_value_after_loop( + &self, + err: &mut Diag<'_>, + expr: &hir::Expr<'tcx>, + expected: Ty<'tcx>, + ) -> bool { + let hir = self.tcx.hir(); + let enclosing_scope = + hir.get_enclosing_scope(expr.hir_id).map(|hir_id| self.tcx.hir_node(hir_id)); + + // Get tail expr of the enclosing block or body + let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope + && expr.is_some() + { + *expr + } else { + let body_def_id = hir.enclosing_body_owner(expr.hir_id); + let body_id = hir.body_owned_by(body_def_id); + let body = hir.body(body_id); + + // Get tail expr of the body + match body.value.kind { + // Regular function body etc. + hir::ExprKind::Block(block, _) => block.expr, + // Anon const body (there's no block in this case) + hir::ExprKind::DropTemps(expr) => Some(expr), + _ => None, + } + }; + + let Some(tail_expr) = tail_expr else { + return false; // Body doesn't have a tail expr we can compare with + }; + + // Get the loop expr within the tail expr + let loop_expr_in_tail = match expr.kind { + hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr, + hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => { + match tail_expr.peel_drop_temps() { + Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body, + _ => return false, // Not really a for loop + } + } + _ => return false, // Not a while or a for loop + }; + + // If the expr is the loop expr in the tail + // then make the suggestion + if expr.hir_id == loop_expr_in_tail.hir_id { + let span = expr.span; + + let (msg, suggestion) = if expected.is_never() { + ( + "consider adding a diverging expression here", + "`loop {}` or `panic!(\"...\")`".to_string(), + ) + } else { + ("consider returning a value here", format!("`{expected}` value")) + }; + + let src_map = self.tcx.sess.source_map(); + let suggestion = if src_map.is_multiline(expr.span) { + let indentation = src_map.indentation_before(span).unwrap_or_else(String::new); + format!("\n{indentation}/* {suggestion} */") + } else { + // If the entire expr is on a single line + // put the suggestion also on the same line + format!(" /* {suggestion} */") + }; + + err.span_suggestion_verbose( + span.shrink_to_hi(), + msg, + suggestion, + Applicability::MaybeIncorrect, + ); + + true + } else { + false + } + } + /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) pub(crate) fn suggest_compatible_variants( diff --git a/tests/ui/coercion/coerce-loop-issue-122561.rs b/tests/ui/coercion/coerce-loop-issue-122561.rs new file mode 100644 index 0000000000000..e08884ad6a4a2 --- /dev/null +++ b/tests/ui/coercion/coerce-loop-issue-122561.rs @@ -0,0 +1,110 @@ +// Regression test for #122561 + +fn for_infinite() -> bool { + for i in 0.. { + //~^ ERROR mismatched types + return false; + } +} + +fn for_finite() -> String { + for i in 0..5 { + //~^ ERROR mismatched types + return String::from("test"); + } +} + +fn for_zero_times() -> bool { + for i in 0..0 { + //~^ ERROR mismatched types + return true; + } +} + +fn for_never_type() -> ! { + for i in 0..5 { + //~^ ERROR mismatched types + } +} + +// Entire function on a single line. +// Tests that we format the suggestion +// correctly in this case +fn for_single_line() -> bool { for i in 0.. { return false; } } +//~^ ERROR mismatched types + +// Loop in an anon const in function args +// Tests that we: +// a. deal properly with this complex case +// b. format the suggestion correctly so +// that it's readable +fn for_in_arg(a: &[(); for x in 0..2 {}]) -> bool { +//~^ ERROR `for` is not allowed in a `const` +//~| ERROR mismatched types + true +} + +fn while_inifinite() -> bool { + while true { + //~^ ERROR mismatched types + //~| WARN denote infinite loops with `loop { ... }` [while_true] + return true; + } +} + +fn while_finite() -> bool { + let mut i = 0; + while i < 3 { + //~^ ERROR mismatched types + i += 1; + return true; + } +} + +fn while_zero_times() -> bool { + while false { + //~^ ERROR mismatched types + return true; + } +} + +fn while_never_type() -> ! { + while true { + //~^ ERROR mismatched types + //~| WARN denote infinite loops with `loop { ... }` [while_true] + } +} + +// No type mismatch error in this case +fn loop_() -> bool { + loop { + return true; + } +} + +const C: i32 = { + for i in 0.. { + //~^ ERROR `for` is not allowed in a `const` + //~| ERROR mismatched types + } +}; + +fn main() { + let _ = [10; { + for i in 0..5 { + //~^ ERROR `for` is not allowed in a `const` + //~| ERROR mismatched types + } + }]; + + let _ = [10; { + while false { + //~^ ERROR mismatched types + } + }]; + + + let _ = |a: &[(); for x in 0..2 {}]| {}; + //~^ ERROR `for` is not allowed in a `const` + //~| ERROR mismatched types +} diff --git a/tests/ui/coercion/coerce-loop-issue-122561.stderr b/tests/ui/coercion/coerce-loop-issue-122561.stderr new file mode 100644 index 0000000000000..0f77fd1364d6b --- /dev/null +++ b/tests/ui/coercion/coerce-loop-issue-122561.stderr @@ -0,0 +1,299 @@ +warning: denote infinite loops with `loop { ... }` + --> $DIR/coerce-loop-issue-122561.rs:48:5 + | +LL | while true { + | ^^^^^^^^^^ help: use `loop` + | + = note: `#[warn(while_true)]` on by default + +warning: denote infinite loops with `loop { ... }` + --> $DIR/coerce-loop-issue-122561.rs:72:5 + | +LL | while true { + | ^^^^^^^^^^ help: use `loop` + +error[E0658]: `for` is not allowed in a `const` + --> $DIR/coerce-loop-issue-122561.rs:41:24 + | +LL | fn for_in_arg(a: &[(); for x in 0..2 {}]) -> bool { + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #87575 for more information + = help: add `#![feature(const_for)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `for` is not allowed in a `const` + --> $DIR/coerce-loop-issue-122561.rs:86:5 + | +LL | / for i in 0.. { +LL | | +LL | | +LL | | } + | |_____^ + | + = note: see issue #87575 for more information + = help: add `#![feature(const_for)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `for` is not allowed in a `const` + --> $DIR/coerce-loop-issue-122561.rs:94:9 + | +LL | / for i in 0..5 { +LL | | +LL | | +LL | | } + | |_________^ + | + = note: see issue #87575 for more information + = help: add `#![feature(const_for)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `for` is not allowed in a `const` + --> $DIR/coerce-loop-issue-122561.rs:107:23 + | +LL | let _ = |a: &[(); for x in 0..2 {}]| {}; + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #87575 for more information + = help: add `#![feature(const_for)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:41:24 + | +LL | fn for_in_arg(a: &[(); for x in 0..2 {}]) -> bool { + | ^^^^^^^^^^^^^^^^ expected `usize`, found `()` + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL | fn for_in_arg(a: &[(); for x in 0..2 {} /* `usize` value */]) -> bool { + | +++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:86:5 + | +LL | / for i in 0.. { +LL | | +LL | | +LL | | } + | |_____^ expected `i32`, found `()` + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `i32` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:4:5 + | +LL | fn for_infinite() -> bool { + | ---- expected `bool` because of return type +LL | / for i in 0.. { +LL | | +LL | | return false; +LL | | } + | |_____^ expected `bool`, found `()` + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `bool` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:11:5 + | +LL | fn for_finite() -> String { + | ------ expected `String` because of return type +LL | / for i in 0..5 { +LL | | +LL | | return String::from("test"); +LL | | } + | |_____^ expected `String`, found `()` + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `String` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:18:5 + | +LL | fn for_zero_times() -> bool { + | ---- expected `bool` because of return type +LL | / for i in 0..0 { +LL | | +LL | | return true; +LL | | } + | |_____^ expected `bool`, found `()` + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `bool` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:25:5 + | +LL | fn for_never_type() -> ! { + | - expected `!` because of return type +LL | / for i in 0..5 { +LL | | +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + = note: `for` loops evaluate to unit type `()` +help: consider adding a diverging expression here + | +LL ~ } +LL + /* `loop {}` or `panic!("...")` */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:33:32 + | +LL | fn for_single_line() -> bool { for i in 0.. { return false; } } + | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | | + | expected `bool` because of return type + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL | fn for_single_line() -> bool { for i in 0.. { return false; } /* `bool` value */ } + | ++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:48:5 + | +LL | fn while_inifinite() -> bool { + | ---- expected `bool` because of return type +LL | / while true { +LL | | +LL | | +LL | | return true; +LL | | } + | |_____^ expected `bool`, found `()` + | + = note: `while` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `bool` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:57:5 + | +LL | fn while_finite() -> bool { + | ---- expected `bool` because of return type +LL | let mut i = 0; +LL | / while i < 3 { +LL | | +LL | | i += 1; +LL | | return true; +LL | | } + | |_____^ expected `bool`, found `()` + | + = note: `while` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `bool` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:65:5 + | +LL | fn while_zero_times() -> bool { + | ---- expected `bool` because of return type +LL | / while false { +LL | | +LL | | return true; +LL | | } + | |_____^ expected `bool`, found `()` + | + = note: `while` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `bool` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:72:5 + | +LL | fn while_never_type() -> ! { + | - expected `!` because of return type +LL | / while true { +LL | | +LL | | +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + = note: `while` loops evaluate to unit type `()` +help: consider adding a diverging expression here + | +LL ~ } +LL + /* `loop {}` or `panic!("...")` */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:94:9 + | +LL | / for i in 0..5 { +LL | | +LL | | +LL | | } + | |_________^ expected `usize`, found `()` + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `usize` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:101:9 + | +LL | / while false { +LL | | +LL | | } + | |_________^ expected `usize`, found `()` + | + = note: `while` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `usize` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:107:23 + | +LL | let _ = |a: &[(); for x in 0..2 {}]| {}; + | ^^^^^^^^^^^^^^^^ expected `usize`, found `()` + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL | let _ = |a: &[(); for x in 0..2 {} /* `usize` value */]| {}; + | +++++++++++++++++++ + +error: aborting due to 18 previous errors; 2 warnings emitted + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/did_you_mean/compatible-variants.stderr b/tests/ui/did_you_mean/compatible-variants.stderr index f2bbd8ced8f24..2c75537ca194f 100644 --- a/tests/ui/did_you_mean/compatible-variants.stderr +++ b/tests/ui/did_you_mean/compatible-variants.stderr @@ -11,6 +11,7 @@ LL | | } | = note: expected enum `Option<()>` found unit type `()` + = note: `while` loops evaluate to unit type `()` help: try adding an expression at the end of the block | LL ~ } @@ -49,6 +50,7 @@ LL | | } | = note: expected enum `Option<()>` found unit type `()` + = note: `for` loops evaluate to unit type `()` help: try adding an expression at the end of the block | LL ~ } @@ -106,6 +108,7 @@ LL | while false {} | = note: expected enum `Option<()>` found unit type `()` + = note: `while` loops evaluate to unit type `()` help: try adding an expression at the end of the block | LL ~ while false {} diff --git a/tests/ui/for-loop-while/break-while-condition.stderr b/tests/ui/for-loop-while/break-while-condition.stderr index 48b29f44fa106..07a57424575fd 100644 --- a/tests/ui/for-loop-while/break-while-condition.stderr +++ b/tests/ui/for-loop-while/break-while-condition.stderr @@ -20,6 +20,12 @@ LL | | } | = note: expected type `!` found unit type `()` + = note: `while` loops evaluate to unit type `()` +help: consider adding a diverging expression here + | +LL ~ } +LL + /* `loop {}` or `panic!("...")` */ + | error[E0308]: mismatched types --> $DIR/break-while-condition.rs:24:13 @@ -31,14 +37,12 @@ LL | | } | = note: expected type `!` found unit type `()` -note: the function expects a value to always be returned, but loops might run zero times - --> $DIR/break-while-condition.rs:24:13 + = note: `while` loops evaluate to unit type `()` +help: consider adding a diverging expression here + | +LL ~ } +LL + /* `loop {}` or `panic!("...")` */ | -LL | while false { - | ^^^^^^^^^^^ this might have zero elements to iterate on -LL | return - | ------ if the loop doesn't execute, this value would never get returned - = help: return a value for the case when the loop has zero elements to iterate on, otherwise consider changing the return type to account for that possibility error: aborting due to 3 previous errors diff --git a/tests/ui/issues/issue-27042.stderr b/tests/ui/issues/issue-27042.stderr index ba39399e46eda..6586e61f2f644 100644 --- a/tests/ui/issues/issue-27042.stderr +++ b/tests/ui/issues/issue-27042.stderr @@ -40,6 +40,8 @@ error[E0308]: mismatched types LL | / 'c: LL | | for _ in None { break }; // but here we cite the whole loop | |_______________________________^ expected `i32`, found `()` + | + = note: `for` loops evaluate to unit type `()` error[E0308]: mismatched types --> $DIR/issue-27042.rs:15:9 diff --git a/tests/ui/issues/issue-50585.stderr b/tests/ui/issues/issue-50585.stderr index 13181f1cf7fe4..e7f13e634750c 100644 --- a/tests/ui/issues/issue-50585.stderr +++ b/tests/ui/issues/issue-50585.stderr @@ -13,6 +13,12 @@ error[E0308]: mismatched types | LL | |y: Vec<[(); for x in 0..2 {}]>| {}; | ^^^^^^^^^^^^^^^^ expected `usize`, found `()` + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL | |y: Vec<[(); for x in 0..2 {} /* `usize` value */]>| {}; + | +++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/issue-100285.rs b/tests/ui/typeck/issue-100285.rs index 460e0457105f5..bea4b2bc2bbe7 100644 --- a/tests/ui/typeck/issue-100285.rs +++ b/tests/ui/typeck/issue-100285.rs @@ -1,4 +1,4 @@ -fn foo(n: i32) -> i32 { //~ HELP otherwise consider changing the return type to account for that possibility +fn foo(n: i32) -> i32 { for i in 0..0 { //~ ERROR mismatched types [E0308] if n < 0 { return i; @@ -14,7 +14,7 @@ fn foo(n: i32) -> i32 { //~ HELP otherwise consider changing the return type to return 5; } - } //~ HELP return a value for the case when the loop has zero elements to iterate on + } //~ 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 9c8685a77127b..6f86fd18e0f68 100644 --- a/tests/ui/typeck/issue-100285.stderr +++ b/tests/ui/typeck/issue-100285.stderr @@ -12,47 +12,12 @@ LL | | LL | | } | |_____^ expected `i32`, found `()` | -note: the function expects a value to always be returned, but loops might run zero times - --> $DIR/issue-100285.rs:2:5 - | -LL | for i in 0..0 { - | ^^^^^^^^^^^^^ this might have zero elements to iterate on -LL | if n < 0 { -LL | return i; - | -------- if the loop doesn't execute, this value would never get returned -LL | } else if n < 10 { -LL | return 1; - | -------- if the loop doesn't execute, this value would never get returned -LL | } else if n < 20 { -LL | return 2; - | -------- if the loop doesn't execute, this value would never get returned - = note: if the loop doesn't execute, 3 other values would never get returned -help: return a value for the case when the loop has zero elements to iterate on + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here | LL ~ } LL ~ /* `i32` value */ | -help: otherwise consider changing the return type to account for that possibility - | -LL ~ fn foo(n: i32) -> Option { -LL | for i in 0..0 { -LL | if n < 0 { -LL ~ return Some(i); -LL | } else if n < 10 { -LL ~ return Some(1); -LL | } else if n < 20 { -LL ~ return Some(2); -LL | } else if n < 30 { -LL ~ return Some(3); -LL | } else if n < 40 { -LL ~ return Some(4); -LL | } else { -LL ~ return Some(5); -LL | } -LL | -LL ~ } -LL ~ None - | error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-98982.rs b/tests/ui/typeck/issue-98982.rs index f875d20fa4c53..3eff9fbe360b7 100644 --- a/tests/ui/typeck/issue-98982.rs +++ b/tests/ui/typeck/issue-98982.rs @@ -1,7 +1,7 @@ -fn foo() -> i32 { //~ HELP otherwise consider changing the return type to account for that possibility +fn foo() -> i32 { for i in 0..0 { //~ ERROR mismatched types [E0308] return i; - } //~ HELP return a value for the case when the loop has zero elements to iterate on + } //~ 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 d8d5a86b157f2..3d40ff4d5afbe 100644 --- a/tests/ui/typeck/issue-98982.stderr +++ b/tests/ui/typeck/issue-98982.stderr @@ -8,26 +8,12 @@ LL | | return i; LL | | } | |_____^ expected `i32`, found `()` | -note: the function expects a value to always be returned, but loops might run zero times - --> $DIR/issue-98982.rs:2:5 - | -LL | for i in 0..0 { - | ^^^^^^^^^^^^^ this might have zero elements to iterate on -LL | return i; - | -------- if the loop doesn't execute, this value would never get returned -help: return a value for the case when the loop has zero elements to iterate on + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here | LL ~ } LL ~ /* `i32` value */ | -help: otherwise consider changing the return type to account for that possibility - | -LL ~ fn foo() -> Option { -LL | for i in 0..0 { -LL ~ return Some(i); -LL ~ } -LL ~ None - | error: aborting due to 1 previous error