@@ -261,6 +261,7 @@ public function __construct(
261261 private readonly bool $ treatPhpDocTypesAsCertain ,
262262 private readonly bool $ detectDeadTypeInMultiCatch ,
263263 private readonly bool $ paramOutType ,
264+ private readonly bool $ preciseMissingReturn ,
264265 )
265266 {
266267 $ earlyTerminatingMethodNames = [];
@@ -360,18 +361,38 @@ public function processStmtNodes(
360361 if ($ shouldCheckLastStatement && $ isLast ) {
361362 /** @var Node\Stmt\Function_|Node\Stmt\ClassMethod|Expr\Closure $parentNode */
362363 $ parentNode = $ parentNode ;
363- $ nodeCallback (new ExecutionEndNode (
364- $ stmt ,
365- new StatementResult (
366- $ scope ,
367- $ hasYield ,
368- $ statementResult ->isAlwaysTerminating (),
369- $ statementResult ->getExitPoints (),
370- $ statementResult ->getThrowPoints (),
371- $ statementResult ->getImpurePoints (),
372- ),
373- $ parentNode ->returnType !== null ,
374- ), $ scope );
364+
365+ $ endStatements = $ statementResult ->getEndStatements ();
366+ if ($ this ->preciseMissingReturn && count ($ endStatements ) > 0 ) {
367+ foreach ($ endStatements as $ endStatement ) {
368+ $ endStatementResult = $ endStatement ->getResult ();
369+ $ nodeCallback (new ExecutionEndNode (
370+ $ endStatement ->getStatement (),
371+ new StatementResult (
372+ $ endStatementResult ->getScope (),
373+ $ hasYield ,
374+ $ endStatementResult ->isAlwaysTerminating (),
375+ $ endStatementResult ->getExitPoints (),
376+ $ endStatementResult ->getThrowPoints (),
377+ $ endStatementResult ->getImpurePoints (),
378+ ),
379+ $ parentNode ->returnType !== null ,
380+ ), $ endStatementResult ->getScope ());
381+ }
382+ } else {
383+ $ nodeCallback (new ExecutionEndNode (
384+ $ stmt ,
385+ new StatementResult (
386+ $ scope ,
387+ $ hasYield ,
388+ $ statementResult ->isAlwaysTerminating (),
389+ $ statementResult ->getExitPoints (),
390+ $ statementResult ->getThrowPoints (),
391+ $ statementResult ->getImpurePoints (),
392+ ),
393+ $ parentNode ->returnType !== null ,
394+ ), $ scope );
395+ }
375396 }
376397
377398 $ exitPoints = array_merge ($ exitPoints , $ statementResult ->getExitPoints ());
@@ -915,6 +936,7 @@ private function processStmtNode(
915936 $ exitPoints = [];
916937 $ throwPoints = $ overridingThrowPoints ?? $ condResult ->getThrowPoints ();
917938 $ impurePoints = $ condResult ->getImpurePoints ();
939+ $ endStatements = [];
918940 $ finalScope = null ;
919941 $ alwaysTerminating = true ;
920942 $ hasYield = $ condResult ->hasYield ();
@@ -928,6 +950,13 @@ private function processStmtNode(
928950 $ branchScope = $ branchScopeStatementResult ->getScope ();
929951 $ finalScope = $ branchScopeStatementResult ->isAlwaysTerminating () ? null : $ branchScope ;
930952 $ alwaysTerminating = $ branchScopeStatementResult ->isAlwaysTerminating ();
953+ if (count ($ branchScopeStatementResult ->getEndStatements ()) > 0 ) {
954+ $ endStatements = array_merge ($ endStatements , $ branchScopeStatementResult ->getEndStatements ());
955+ } elseif (count ($ stmt ->stmts ) > 0 ) {
956+ $ endStatements [] = new EndStatementResult ($ stmt ->stmts [count ($ stmt ->stmts ) - 1 ], $ branchScopeStatementResult );
957+ } else {
958+ $ endStatements [] = new EndStatementResult ($ stmt , $ branchScopeStatementResult );
959+ }
931960 $ hasYield = $ branchScopeStatementResult ->hasYield () || $ hasYield ;
932961 }
933962
@@ -960,6 +989,13 @@ private function processStmtNode(
960989 $ branchScope = $ branchScopeStatementResult ->getScope ();
961990 $ finalScope = $ branchScopeStatementResult ->isAlwaysTerminating () ? $ finalScope : $ branchScope ->mergeWith ($ finalScope );
962991 $ alwaysTerminating = $ alwaysTerminating && $ branchScopeStatementResult ->isAlwaysTerminating ();
992+ if (count ($ branchScopeStatementResult ->getEndStatements ()) > 0 ) {
993+ $ endStatements = array_merge ($ endStatements , $ branchScopeStatementResult ->getEndStatements ());
994+ } elseif (count ($ elseif ->stmts ) > 0 ) {
995+ $ endStatements [] = new EndStatementResult ($ elseif ->stmts [count ($ elseif ->stmts ) - 1 ], $ branchScopeStatementResult );
996+ } else {
997+ $ endStatements [] = new EndStatementResult ($ elseif , $ branchScopeStatementResult );
998+ }
963999 $ hasYield = $ hasYield || $ branchScopeStatementResult ->hasYield ();
9641000 }
9651001
@@ -989,6 +1025,13 @@ private function processStmtNode(
9891025 $ branchScope = $ branchScopeStatementResult ->getScope ();
9901026 $ finalScope = $ branchScopeStatementResult ->isAlwaysTerminating () ? $ finalScope : $ branchScope ->mergeWith ($ finalScope );
9911027 $ alwaysTerminating = $ alwaysTerminating && $ branchScopeStatementResult ->isAlwaysTerminating ();
1028+ if (count ($ branchScopeStatementResult ->getEndStatements ()) > 0 ) {
1029+ $ endStatements = array_merge ($ endStatements , $ branchScopeStatementResult ->getEndStatements ());
1030+ } elseif (count ($ stmt ->else ->stmts ) > 0 ) {
1031+ $ endStatements [] = new EndStatementResult ($ stmt ->else ->stmts [count ($ stmt ->else ->stmts ) - 1 ], $ branchScopeStatementResult );
1032+ } else {
1033+ $ endStatements [] = new EndStatementResult ($ stmt ->else , $ branchScopeStatementResult );
1034+ }
9921035 $ hasYield = $ hasYield || $ branchScopeStatementResult ->hasYield ();
9931036 }
9941037 }
@@ -997,7 +1040,11 @@ private function processStmtNode(
9971040 $ finalScope = $ scope ;
9981041 }
9991042
1000- return new StatementResult ($ finalScope , $ hasYield , $ alwaysTerminating , $ exitPoints , $ throwPoints , $ impurePoints );
1043+ if ($ stmt ->else === null && !$ ifAlwaysTrue && !$ lastElseIfConditionIsTrue ) {
1044+ $ endStatements [] = new EndStatementResult ($ stmt , new StatementResult ($ finalScope , $ hasYield , $ alwaysTerminating , $ exitPoints , $ throwPoints , $ impurePoints ));
1045+ }
1046+
1047+ return new StatementResult ($ finalScope , $ hasYield , $ alwaysTerminating , $ exitPoints , $ throwPoints , $ impurePoints , $ endStatements );
10011048 } elseif ($ stmt instanceof Node \Stmt \TraitUse) {
10021049 $ hasYield = false ;
10031050 $ throwPoints = [];
0 commit comments