3737
3838use crate :: errors:: SuggestBoxingForReturnImplTrait ;
3939use crate :: FnCtxt ;
40- use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , Diag , MultiSpan } ;
40+ use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , Diag } ;
4141use rustc_hir as hir;
4242use rustc_hir:: def_id:: { DefId , LocalDefId } ;
43- use rustc_hir:: intravisit:: { self , Visitor } ;
44- use rustc_hir:: Expr ;
4543use rustc_hir_analysis:: hir_ty_lowering:: HirTyLowerer ;
4644use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
4745use rustc_infer:: infer:: { Coercion , DefineOpaqueTypes , InferOk , InferResult } ;
@@ -95,22 +93,6 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
9593
9694type CoerceResult < ' tcx > = InferResult < ' tcx , ( Vec < Adjustment < ' tcx > > , Ty < ' tcx > ) > ;
9795
98- struct CollectRetsVisitor < ' tcx > {
99- ret_exprs : Vec < & ' tcx hir:: Expr < ' tcx > > ,
100- }
101-
102- impl < ' tcx > Visitor < ' tcx > for CollectRetsVisitor < ' tcx > {
103- fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
104- match expr. kind {
105- hir:: ExprKind :: Ret ( _) => self . ret_exprs . push ( expr) ,
106- // `return` in closures does not return from the outer function
107- hir:: ExprKind :: Closure ( _) => return ,
108- _ => { }
109- }
110- intravisit:: walk_expr ( self , expr) ;
111- }
112- }
113-
11496/// Coercing a mutable reference to an immutable works, while
11597/// coercing `&T` to `&mut T` should be forbidden.
11698fn coerce_mutbls < ' tcx > (
@@ -1593,7 +1575,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15931575
15941576 let mut err;
15951577 let mut unsized_return = false ;
1596- let mut visitor = CollectRetsVisitor { ret_exprs : vec ! [ ] } ;
15971578 match * cause. code ( ) {
15981579 ObligationCauseCode :: ReturnNoExpression => {
15991580 err = struct_span_code_err ! (
@@ -1619,11 +1600,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16191600 if !fcx. tcx . features ( ) . unsized_locals {
16201601 unsized_return = self . is_return_ty_definitely_unsized ( fcx) ;
16211602 }
1622- if let Some ( expression) = expression
1623- && let hir:: ExprKind :: Loop ( loop_blk, ..) = expression. kind
1624- {
1625- intravisit:: walk_block ( & mut visitor, loop_blk) ;
1626- }
16271603 }
16281604 ObligationCauseCode :: ReturnValue ( id) => {
16291605 err = self . report_return_mismatched_types (
@@ -1732,15 +1708,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17321708 None ,
17331709 Some ( coercion_error) ,
17341710 ) ;
1735- if visitor. ret_exprs . len ( ) > 0 {
1736- self . note_unreachable_loop_return (
1737- & mut err,
1738- fcx. tcx ,
1739- & expr,
1740- & visitor. ret_exprs ,
1741- expected,
1742- ) ;
1743- }
1711+
1712+ self . note_unreachable_loop_return ( & mut err, fcx. tcx , & expr, expected) ;
17441713 }
17451714
17461715 let reported = err. emit_unless ( unsized_return) ;
@@ -1819,102 +1788,69 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
18191788 err : & mut Diag < ' _ > ,
18201789 tcx : TyCtxt < ' tcx > ,
18211790 expr : & hir:: Expr < ' tcx > ,
1822- ret_exprs : & Vec < & ' tcx hir:: Expr < ' tcx > > ,
18231791 ty : Ty < ' tcx > ,
18241792 ) {
1825- let hir:: ExprKind :: Loop ( _, _, _ , loop_span ) = expr. kind else {
1793+ let hir:: ExprKind :: Loop ( _, _, loop_src , _ ) = expr. kind else {
18261794 return ;
18271795 } ;
1828- let mut span: MultiSpan = vec ! [ loop_span] . into ( ) ;
1829- span. push_span_label ( loop_span, "this might have zero elements to iterate on" ) ;
1830- const MAXITER : usize = 3 ;
1831- let iter = ret_exprs. iter ( ) . take ( MAXITER ) ;
1832- for ret_expr in iter {
1833- span. push_span_label (
1834- ret_expr. span ,
1835- "if the loop doesn't execute, this value would never get returned" ,
1836- ) ;
1837- }
1838- err. span_note (
1839- span,
1840- "the function expects a value to always be returned, but loops might run zero times" ,
1841- ) ;
1842- if MAXITER < ret_exprs. len ( ) {
1843- err. note ( format ! (
1844- "if the loop doesn't execute, {} other values would never get returned" ,
1845- ret_exprs. len( ) - MAXITER
1846- ) ) ;
1847- }
1796+
18481797 let hir = tcx. hir ( ) ;
1849- let item = hir. get_parent_item ( expr. hir_id ) ;
1850- let ret_msg = "return a value for the case when the loop has zero elements to iterate on" ;
1851- let ret_ty_msg =
1852- "otherwise consider changing the return type to account for that possibility" ;
1853- let node = tcx. hir_node ( item. into ( ) ) ;
1854- if let Some ( body_id) = node. body_id ( )
1855- && let Some ( sig) = node. fn_sig ( )
1856- && let hir:: ExprKind :: Block ( block, _) = hir. body ( body_id) . value . kind
1857- && !ty. is_never ( )
1858- {
1859- let indentation = if let None = block. expr
1860- && let [ .., last] = & block. stmts
1861- {
1862- tcx. sess . source_map ( ) . indentation_before ( last. span ) . unwrap_or_else ( String :: new)
1863- } else if let Some ( expr) = block. expr {
1864- tcx. sess . source_map ( ) . indentation_before ( expr. span ) . unwrap_or_else ( String :: new)
1798+ let body_def_id = hir. enclosing_body_owner ( expr. hir_id ) ;
1799+ let body_id = hir. body_owned_by ( body_def_id) ;
1800+ let body = hir. body ( body_id) ;
1801+
1802+ let span = match body. value . kind {
1803+ // Regular block as in a function body
1804+ hir:: ExprKind :: Block ( block, _) => {
1805+ if let None = block. expr
1806+ && let [ .., last] = & block. stmts
1807+ {
1808+ Some ( last. span )
1809+ } else if let Some ( expr) = block. expr {
1810+ Some ( expr. span )
1811+ } else {
1812+ None
1813+ }
1814+ }
1815+ // Anon const body (there's no enclosing block in this case)
1816+ hir:: ExprKind :: DropTemps ( expr) => Some ( expr. span ) ,
1817+ _ => None ,
1818+ } ;
1819+
1820+ if let Some ( span) = span {
1821+ let ( msg, suggestion) = if ty. is_never ( ) {
1822+ (
1823+ "consider adding a diverging expression here" ,
1824+ "`loop {}` or `panic!(\" ...\" )`" . to_string ( ) ,
1825+ )
18651826 } else {
1866- String :: new ( )
1827+ ( "consider returning a value here" , format ! ( "`{ty}` value" ) )
18671828 } ;
1868- if let None = block. expr
1869- && let [ .., last] = & block. stmts
1870- {
1871- err. span_suggestion_verbose (
1872- last. span . shrink_to_hi ( ) ,
1873- ret_msg,
1874- format ! ( "\n {indentation}/* `{ty}` value */" ) ,
1875- Applicability :: MaybeIncorrect ,
1876- ) ;
1877- } else if let Some ( expr) = block. expr {
1878- err. span_suggestion_verbose (
1879- expr. span . shrink_to_hi ( ) ,
1880- ret_msg,
1881- format ! ( "\n {indentation}/* `{ty}` value */" ) ,
1882- Applicability :: MaybeIncorrect ,
1883- ) ;
1884- }
1885- let mut sugg = match sig. decl . output {
1886- hir:: FnRetTy :: DefaultReturn ( span) => {
1887- vec ! [ ( span, " -> Option<()>" . to_string( ) ) ]
1888- }
1889- hir:: FnRetTy :: Return ( ty) => {
1890- vec ! [
1891- ( ty. span. shrink_to_lo( ) , "Option<" . to_string( ) ) ,
1892- ( ty. span. shrink_to_hi( ) , ">" . to_string( ) ) ,
1893- ]
1894- }
1829+
1830+ let src_map = tcx. sess . source_map ( ) ;
1831+ let suggestion = if src_map. is_multiline ( expr. span ) {
1832+ let indentation = src_map. indentation_before ( span) . unwrap_or_else ( String :: new) ;
1833+ format ! ( "\n {indentation}/* {suggestion} */" )
1834+ } else {
1835+ // If the entire expr is on a single line
1836+ // put the suggestion also on the same line
1837+ format ! ( " /* {suggestion} */" )
18951838 } ;
1896- for ret_expr in ret_exprs {
1897- match ret_expr. kind {
1898- hir:: ExprKind :: Ret ( Some ( expr) ) => {
1899- sugg. push ( ( expr. span . shrink_to_lo ( ) , "Some(" . to_string ( ) ) ) ;
1900- sugg. push ( ( expr. span . shrink_to_hi ( ) , ")" . to_string ( ) ) ) ;
1901- }
1902- hir:: ExprKind :: Ret ( None ) => {
1903- sugg. push ( ( ret_expr. span . shrink_to_hi ( ) , " Some(())" . to_string ( ) ) ) ;
1904- }
1905- _ => { }
1906- }
1907- }
1908- if let None = block. expr
1909- && let [ .., last] = & block. stmts
1910- {
1911- sugg. push ( ( last. span . shrink_to_hi ( ) , format ! ( "\n {indentation}None" ) ) ) ;
1912- } else if let Some ( expr) = block. expr {
1913- sugg. push ( ( expr. span . shrink_to_hi ( ) , format ! ( "\n {indentation}None" ) ) ) ;
1914- }
1915- err. multipart_suggestion ( ret_ty_msg, sugg, Applicability :: MaybeIncorrect ) ;
1916- } else {
1917- err. help ( format ! ( "{ret_msg}, {ret_ty_msg}" ) ) ;
1839+
1840+ let loop_type = match loop_src {
1841+ hir:: LoopSource :: Loop => return , // Plain loops do not run into type mismatch error
1842+ hir:: LoopSource :: While => "`while` loops" ,
1843+ hir:: LoopSource :: ForLoop => "`for` loops" ,
1844+ } ;
1845+
1846+ err. note ( format ! ( "{loop_type} evaluate to unit type `()`" ) ) ;
1847+
1848+ err. span_suggestion_verbose (
1849+ span. shrink_to_hi ( ) ,
1850+ msg,
1851+ suggestion,
1852+ Applicability :: MaybeIncorrect ,
1853+ ) ;
19181854 }
19191855 }
19201856
0 commit comments