@@ -484,8 +484,10 @@ impl<'a> BindingsBuilder<'a> {
484484 /// This function must not be called unless the function body statements will be bound;
485485 /// it relies on that binding to ensure we don't have a dangling `Idx<Key>` (which could lead
486486 /// to a panic).
487- fn implicit_return ( & mut self , body : & [ Stmt ] , func_name : & Identifier ) -> Idx < Key > {
488- let last_exprs = function_last_expressions ( body, self . sys_info ) . map ( |x| {
487+ fn implicit_return ( & mut self , body : & [ Stmt ] , func_name : & Identifier ) -> ( Idx < Key > , bool ) {
488+ let last_exprs = function_last_expressions ( body, self . sys_info ) ;
489+ let has_infinite_loop = last_exprs. as_ref ( ) . is_some_and ( |x| x. has_infinite_loop ) ;
490+ let last_exprs = last_exprs. map ( |x| {
489491 x. exprs
490492 . into_map ( |( last, x) | {
491493 (
@@ -495,9 +497,12 @@ impl<'a> BindingsBuilder<'a> {
495497 } )
496498 . into_boxed_slice ( )
497499 } ) ;
498- self . insert_binding (
499- Key :: ReturnImplicit ( ShortIdentifier :: new ( func_name) ) ,
500- Binding :: ReturnImplicit ( ReturnImplicit { last_exprs } ) ,
500+ (
501+ self . insert_binding (
502+ Key :: ReturnImplicit ( ShortIdentifier :: new ( func_name) ) ,
503+ Binding :: ReturnImplicit ( ReturnImplicit { last_exprs } ) ,
504+ ) ,
505+ has_infinite_loop,
501506 )
502507 }
503508
@@ -509,7 +514,7 @@ impl<'a> BindingsBuilder<'a> {
509514 is_async : bool ,
510515 yields_and_returns : YieldsAndReturns ,
511516 return_ann_with_range : Option < ( TextRange , Idx < KeyAnnotation > ) > ,
512- implicit_return : Option < Idx < Key > > ,
517+ implicit_return : Option < ( Idx < Key > , bool ) > ,
513518 should_infer_return_type : bool ,
514519 is_stub : bool ,
515520 ) {
@@ -521,6 +526,10 @@ impl<'a> BindingsBuilder<'a> {
521526 } else {
522527 None
523528 } ;
529+ let has_reachable_return = yields_and_returns
530+ . returns
531+ . iter ( )
532+ . any ( |( _, _, is_unreachable) | !is_unreachable) ;
524533
525534 // Collect the keys of explicit returns.
526535 let return_keys = yields_and_returns
@@ -570,15 +579,20 @@ impl<'a> BindingsBuilder<'a> {
570579
571580 let return_type_binding = {
572581 let kind = match ( return_ann_with_range, implicit_return, is_stub) {
573- ( Some ( ( range, annotation) ) , Some ( implicit_return) , false ) => {
582+ (
583+ Some ( ( range, annotation) ) ,
584+ Some ( ( implicit_return, has_infinite_loop) ) ,
585+ false ,
586+ ) => {
574587 self . insert_binding (
575588 KeyExpect :: ValidateImplicitReturn ( range) ,
576589 BindingExpect :: ValidateImplicitReturn {
577590 annotation,
578591 implicit_return,
579592 is_async,
580593 is_generator,
581- has_explicit_return : !return_keys. is_empty ( ) ,
594+ has_explicit_return : has_reachable_return,
595+ has_infinite_loop,
582596 } ,
583597 ) ;
584598 ReturnTypeKind :: ShouldTrustAnnotation {
@@ -597,7 +611,7 @@ impl<'a> BindingsBuilder<'a> {
597611 is_generator,
598612 }
599613 }
600- ( None , Some ( implicit_return) , _) if should_infer_return_type => {
614+ ( None , Some ( ( implicit_return, _ ) ) , _) if should_infer_return_type => {
601615 // We don't have an explicit return annotation, but we want to infer it.
602616 ReturnTypeKind :: ShouldInferType {
603617 returns : return_keys,
@@ -985,6 +999,8 @@ pub(crate) struct LastExpressions<'a> {
985999 /// settling an assertion. The answer then holds for that configuration only, which suits
9861000 /// inferring a return type but not reporting code as dead.
9871001 pub decided_by_environment : bool ,
1002+ /// Set when an unconditional loop is one of the terminal paths.
1003+ pub has_infinite_loop : bool ,
9881004}
9891005
9901006/// Given the body of a function, what are the potential expressions that
@@ -1002,6 +1018,7 @@ pub(crate) fn function_last_expressions<'a>(
10021018 x : & ' a [ Stmt ] ,
10031019 res : & mut Vec < ( LastStmt , & ' a Expr ) > ,
10041020 environment : & mut bool ,
1021+ has_infinite_loop : & mut bool ,
10051022 ) -> Option < ( ) > {
10061023 fn loop_body_has_break_statement ( statement : & Stmt , has_break : & mut bool ) {
10071024 match statement {
@@ -1025,7 +1042,7 @@ pub(crate) fn function_last_expressions<'a>(
10251042 for y in & x. items {
10261043 res. push ( ( LastStmt :: With ( kind) , & y. context_expr ) ) ;
10271044 }
1028- f ( sys_info, & x. body , res, environment) ?;
1045+ f ( sys_info, & x. body , res, environment, has_infinite_loop ) ?;
10291046 }
10301047 Stmt :: While ( x) => {
10311048 let test_value = sys_info. evaluate_bool ( & x. test ) ;
@@ -1038,10 +1055,11 @@ pub(crate) fn function_last_expressions<'a>(
10381055 }
10391056 if test_value == Some ( true ) && !has_break {
10401057 // Infinite loop with no break never falls through.
1058+ * has_infinite_loop = true ;
10411059 } else if has_break || x. orelse . is_empty ( ) {
10421060 return None ;
10431061 } else {
1044- f ( sys_info, & x. orelse , res, environment) ?;
1062+ f ( sys_info, & x. orelse , res, environment, has_infinite_loop ) ?;
10451063 }
10461064 }
10471065 Stmt :: For ( x) => {
@@ -1051,7 +1069,7 @@ pub(crate) fn function_last_expressions<'a>(
10511069 if has_break || x. orelse . is_empty ( ) {
10521070 return None ;
10531071 }
1054- f ( sys_info, & x. orelse , res, environment) ?;
1072+ f ( sys_info, & x. orelse , res, environment, has_infinite_loop ) ?;
10551073 }
10561074 Stmt :: If ( x) => {
10571075 * environment |= iter:: once ( Some ( & * x. test ) )
@@ -1062,7 +1080,7 @@ pub(crate) fn function_last_expressions<'a>(
10621080 for ( test, body) in sys_info. pruned_if_branches ( x) {
10631081 any_branch_processed = true ;
10641082 last_test = test;
1065- f ( sys_info, body, res, environment) ?;
1083+ f ( sys_info, body, res, environment, has_infinite_loop ) ?;
10661084 }
10671085 if !any_branch_processed {
10681086 // All branches were pruned, so the code falls through
@@ -1087,17 +1105,17 @@ pub(crate) fn function_last_expressions<'a>(
10871105 . iter ( )
10881106 . any ( |stmt| matches ! ( stmt, Stmt :: Return ( _) ) )
10891107 {
1090- f ( sys_info, & x. finalbody , res, environment) ?;
1108+ f ( sys_info, & x. finalbody , res, environment, has_infinite_loop ) ?;
10911109 } else {
10921110 if x. orelse . is_empty ( ) {
1093- f ( sys_info, & x. body , res, environment) ?;
1111+ f ( sys_info, & x. body , res, environment, has_infinite_loop ) ?;
10941112 } else {
1095- f ( sys_info, & x. orelse , res, environment) ?;
1113+ f ( sys_info, & x. orelse , res, environment, has_infinite_loop ) ?;
10961114 }
10971115 for handler in & x. handlers {
10981116 match handler {
10991117 ExceptHandler :: ExceptHandler ( x) => {
1100- f ( sys_info, & x. body , res, environment) ?
1118+ f ( sys_info, & x. body , res, environment, has_infinite_loop ) ?
11011119 }
11021120 }
11031121 }
@@ -1107,7 +1125,7 @@ pub(crate) fn function_last_expressions<'a>(
11071125 Stmt :: Match ( x) => {
11081126 let mut syntactically_exhaustive = false ;
11091127 for case in x. cases . iter ( ) {
1110- f ( sys_info, & case. body , res, environment) ?;
1128+ f ( sys_info, & case. body , res, environment, has_infinite_loop ) ?;
11111129 // Must match the binding step's exhaustiveness judgment in
11121130 // `stmt_match`; otherwise the `Key::Exhaustive(Match, ...)` promised
11131131 // below is never inserted and solve time panics.
@@ -1134,10 +1152,18 @@ pub(crate) fn function_last_expressions<'a>(
11341152
11351153 let mut res = Vec :: new ( ) ;
11361154 let mut environment = false ;
1137- f ( sys_info, x, & mut res, & mut environment) ?;
1155+ let mut has_infinite_loop = false ;
1156+ f (
1157+ sys_info,
1158+ x,
1159+ & mut res,
1160+ & mut environment,
1161+ & mut has_infinite_loop,
1162+ ) ?;
11381163 Some ( LastExpressions {
11391164 exprs : res,
11401165 decided_by_environment : environment,
1166+ has_infinite_loop,
11411167 } )
11421168}
11431169
0 commit comments