171171use PHPStan \Type \ClosureType ;
172172use PHPStan \Type \Constant \ConstantArrayType ;
173173use PHPStan \Type \Constant \ConstantArrayTypeBuilder ;
174- use PHPStan \Type \Constant \ConstantBooleanType ;
175174use PHPStan \Type \Constant \ConstantIntegerType ;
176175use PHPStan \Type \Constant \ConstantStringType ;
177176use PHPStan \Type \ErrorType ;
@@ -1079,7 +1078,7 @@ private function processStmtNode(
10791078 }
10801079 } elseif ($ stmt instanceof If_) {
10811080 $ conditionType = ($ this ->treatPhpDocTypesAsCertain ? $ scope ->getType ($ stmt ->cond ) : $ scope ->getNativeType ($ stmt ->cond ))->toBoolean ();
1082- $ ifAlwaysTrue = $ conditionType ->isTrue ()-> yes () ;
1081+ $ ifAlwaysTrue = $ conditionType ->isTrue ();
10831082 $ condResult = $ this ->processExprNode ($ stmt , $ stmt ->cond , $ scope , $ nodeCallback , ExpressionContext::createDeep (), $ context );
10841083 $ exitPoints = [];
10851084 $ throwPoints = $ overridingThrowPoints ?? $ condResult ->getThrowPoints ();
@@ -1089,47 +1088,54 @@ private function processStmtNode(
10891088 $ alwaysTerminating = true ;
10901089 $ hasYield = $ condResult ->hasYield ();
10911090
1092- $ branchScopeStatementResult = $ this ->processStmtNodes ($ stmt , $ stmt ->stmts , $ condResult ->getTruthyScope (), $ nodeCallback , $ context );
1093-
1094- if (!$ conditionType instanceof ConstantBooleanType || $ conditionType ->getValue ()) {
1095- $ exitPoints = $ branchScopeStatementResult ->getExitPoints ();
1096- $ throwPoints = array_merge ($ throwPoints , $ branchScopeStatementResult ->getThrowPoints ());
1097- $ impurePoints = array_merge ($ impurePoints , $ branchScopeStatementResult ->getImpurePoints ());
1098- $ branchScope = $ branchScopeStatementResult ->getScope ();
1099- $ finalScope = $ branchScopeStatementResult ->isAlwaysTerminating () ? null : $ branchScope ;
1100- $ alwaysTerminating = $ branchScopeStatementResult ->isAlwaysTerminating ();
1101- if (count ($ branchScopeStatementResult ->getEndStatements ()) > 0 ) {
1102- $ endStatements = array_merge ($ endStatements , $ branchScopeStatementResult ->getEndStatements ());
1103- } elseif (count ($ stmt ->stmts ) > 0 ) {
1104- $ endStatements [] = new EndStatementResult ($ stmt ->stmts [count ($ stmt ->stmts ) - 1 ], $ branchScopeStatementResult );
1105- } else {
1106- $ endStatements [] = new EndStatementResult ($ stmt , $ branchScopeStatementResult );
1091+ if ($ context ->isTopLevel () || !$ conditionType ->isTrue ()->no ()) {
1092+ $ branchScopeStatementResult = $ this ->processStmtNodes ($ stmt , $ stmt ->stmts , $ condResult ->getTruthyScope (), $ nodeCallback , $ context );
1093+
1094+ if (!$ conditionType ->isTrue ()->no ()) {
1095+ $ exitPoints = $ branchScopeStatementResult ->getExitPoints ();
1096+ $ throwPoints = array_merge ($ throwPoints , $ branchScopeStatementResult ->getThrowPoints ());
1097+ $ impurePoints = array_merge ($ impurePoints , $ branchScopeStatementResult ->getImpurePoints ());
1098+ $ branchScope = $ branchScopeStatementResult ->getScope ();
1099+ $ finalScope = $ branchScopeStatementResult ->isAlwaysTerminating () ? null : $ branchScope ;
1100+ $ alwaysTerminating = $ branchScopeStatementResult ->isAlwaysTerminating ();
1101+ if (count ($ branchScopeStatementResult ->getEndStatements ()) > 0 ) {
1102+ $ endStatements = array_merge ($ endStatements , $ branchScopeStatementResult ->getEndStatements ());
1103+ } elseif (count ($ stmt ->stmts ) > 0 ) {
1104+ $ endStatements [] = new EndStatementResult ($ stmt ->stmts [count ($ stmt ->stmts ) - 1 ], $ branchScopeStatementResult );
1105+ } else {
1106+ $ endStatements [] = new EndStatementResult ($ stmt , $ branchScopeStatementResult );
1107+ }
1108+ $ hasYield = $ branchScopeStatementResult ->hasYield () || $ hasYield ;
11071109 }
1108- $ hasYield = $ branchScopeStatementResult ->hasYield () || $ hasYield ;
11091110 }
11101111
11111112 $ scope = $ condResult ->getFalseyScope ();
1112- $ lastElseIfConditionIsTrue = false ;
1113+ $ lastElseIfConditionIsTrue = TrinaryLogic:: createNo () ;
11131114
11141115 $ condScope = $ scope ;
11151116 foreach ($ stmt ->elseifs as $ elseif ) {
11161117 $ nodeCallback ($ elseif , $ scope );
1118+ if (!$ context ->isTopLevel ()) {
1119+ if ($ ifAlwaysTrue ->yes () || $ lastElseIfConditionIsTrue ->yes ()) {
1120+ continue ;
1121+ }
1122+ }
11171123 $ elseIfConditionType = ($ this ->treatPhpDocTypesAsCertain ? $ condScope ->getType ($ elseif ->cond ) : $ scope ->getNativeType ($ elseif ->cond ))->toBoolean ();
11181124 $ condResult = $ this ->processExprNode ($ stmt , $ elseif ->cond , $ condScope , $ nodeCallback , ExpressionContext::createDeep (), $ context );
11191125 $ throwPoints = array_merge ($ throwPoints , $ condResult ->getThrowPoints ());
11201126 $ impurePoints = array_merge ($ impurePoints , $ condResult ->getImpurePoints ());
11211127 $ condScope = $ condResult ->getScope ();
11221128 $ branchScopeStatementResult = $ this ->processStmtNodes ($ elseif , $ elseif ->stmts , $ condResult ->getTruthyScope (), $ nodeCallback , $ context );
11231129
1130+ if (!$ context ->isTopLevel () && $ elseIfConditionType ->isTrue ()->no ()) {
1131+ $ scope = $ condScope ->filterByFalseyValue ($ elseif ->cond );
1132+ continue ;
1133+ }
1134+
11241135 if (
1125- !$ ifAlwaysTrue
1126- && (
1127- !$ lastElseIfConditionIsTrue
1128- && (
1129- !$ elseIfConditionType instanceof ConstantBooleanType
1130- || $ elseIfConditionType ->getValue ()
1131- )
1132- )
1136+ !$ ifAlwaysTrue ->yes ()
1137+ && !$ lastElseIfConditionIsTrue ->yes ()
1138+ && !$ elseIfConditionType ->isTrue ()->no ()
11331139 ) {
11341140 $ exitPoints = array_merge ($ exitPoints , $ branchScopeStatementResult ->getExitPoints ());
11351141 $ throwPoints = array_merge ($ throwPoints , $ branchScopeStatementResult ->getThrowPoints ());
@@ -1147,48 +1153,48 @@ private function processStmtNode(
11471153 $ hasYield = $ hasYield || $ branchScopeStatementResult ->hasYield ();
11481154 }
11491155
1150- if (
1151- $ elseIfConditionType ->isTrue ()->yes ()
1152- ) {
1153- $ lastElseIfConditionIsTrue = true ;
1156+ if ($ elseIfConditionType ->isTrue ()->yes ()) {
1157+ $ lastElseIfConditionIsTrue = $ elseIfConditionType ->isTrue ();
11541158 }
1155-
11561159 $ condScope = $ condScope ->filterByFalseyValue ($ elseif ->cond );
11571160 $ scope = $ condScope ;
11581161 }
11591162
11601163 if ($ stmt ->else === null ) {
1161- if (!$ ifAlwaysTrue && !$ lastElseIfConditionIsTrue ) {
1164+ if (!$ ifAlwaysTrue-> yes () && !$ lastElseIfConditionIsTrue-> yes () ) {
11621165 $ finalScope = $ scope ->mergeWith ($ finalScope );
11631166 $ alwaysTerminating = false ;
11641167 }
11651168 } else {
11661169 $ nodeCallback ($ stmt ->else , $ scope );
1167- $ branchScopeStatementResult = $ this ->processStmtNodes ($ stmt ->else , $ stmt ->else ->stmts , $ scope , $ nodeCallback , $ context );
11681170
1169- if (!$ ifAlwaysTrue && !$ lastElseIfConditionIsTrue ) {
1170- $ exitPoints = array_merge ($ exitPoints , $ branchScopeStatementResult ->getExitPoints ());
1171- $ throwPoints = array_merge ($ throwPoints , $ branchScopeStatementResult ->getThrowPoints ());
1172- $ impurePoints = array_merge ($ impurePoints , $ branchScopeStatementResult ->getImpurePoints ());
1173- $ branchScope = $ branchScopeStatementResult ->getScope ();
1174- $ finalScope = $ branchScopeStatementResult ->isAlwaysTerminating () ? $ finalScope : $ branchScope ->mergeWith ($ finalScope );
1175- $ alwaysTerminating = $ alwaysTerminating && $ branchScopeStatementResult ->isAlwaysTerminating ();
1176- if (count ($ branchScopeStatementResult ->getEndStatements ()) > 0 ) {
1177- $ endStatements = array_merge ($ endStatements , $ branchScopeStatementResult ->getEndStatements ());
1178- } elseif (count ($ stmt ->else ->stmts ) > 0 ) {
1179- $ endStatements [] = new EndStatementResult ($ stmt ->else ->stmts [count ($ stmt ->else ->stmts ) - 1 ], $ branchScopeStatementResult );
1180- } else {
1181- $ endStatements [] = new EndStatementResult ($ stmt ->else , $ branchScopeStatementResult );
1171+ if ($ context ->isTopLevel () || (!$ ifAlwaysTrue ->yes () && !$ lastElseIfConditionIsTrue ->yes ())) {
1172+ $ branchScopeStatementResult = $ this ->processStmtNodes ($ stmt ->else , $ stmt ->else ->stmts , $ scope , $ nodeCallback , $ context );
1173+
1174+ if (!$ ifAlwaysTrue ->yes () && !$ lastElseIfConditionIsTrue ->yes ()) {
1175+ $ exitPoints = array_merge ($ exitPoints , $ branchScopeStatementResult ->getExitPoints ());
1176+ $ throwPoints = array_merge ($ throwPoints , $ branchScopeStatementResult ->getThrowPoints ());
1177+ $ impurePoints = array_merge ($ impurePoints , $ branchScopeStatementResult ->getImpurePoints ());
1178+ $ branchScope = $ branchScopeStatementResult ->getScope ();
1179+ $ finalScope = $ branchScopeStatementResult ->isAlwaysTerminating () ? $ finalScope : $ branchScope ->mergeWith ($ finalScope );
1180+ $ alwaysTerminating = $ alwaysTerminating && $ branchScopeStatementResult ->isAlwaysTerminating ();
1181+ if (count ($ branchScopeStatementResult ->getEndStatements ()) > 0 ) {
1182+ $ endStatements = array_merge ($ endStatements , $ branchScopeStatementResult ->getEndStatements ());
1183+ } elseif (count ($ stmt ->else ->stmts ) > 0 ) {
1184+ $ endStatements [] = new EndStatementResult ($ stmt ->else ->stmts [count ($ stmt ->else ->stmts ) - 1 ], $ branchScopeStatementResult );
1185+ } else {
1186+ $ endStatements [] = new EndStatementResult ($ stmt ->else , $ branchScopeStatementResult );
1187+ }
1188+ $ hasYield = $ hasYield || $ branchScopeStatementResult ->hasYield ();
11821189 }
1183- $ hasYield = $ hasYield || $ branchScopeStatementResult ->hasYield ();
11841190 }
11851191 }
11861192
11871193 if ($ finalScope === null ) {
11881194 $ finalScope = $ scope ;
11891195 }
11901196
1191- if ($ stmt ->else === null && !$ ifAlwaysTrue && !$ lastElseIfConditionIsTrue ) {
1197+ if ($ stmt ->else === null && !$ ifAlwaysTrue-> yes () && !$ lastElseIfConditionIsTrue-> yes () ) {
11921198 $ endStatements [] = new EndStatementResult ($ stmt , new StatementResult ($ finalScope , $ hasYield , $ alwaysTerminating , $ exitPoints , $ throwPoints , $ impurePoints ));
11931199 }
11941200
@@ -1202,6 +1208,7 @@ private function processStmtNode(
12021208 $ condResult = $ this ->processExprNode ($ stmt , $ stmt ->expr , $ scope , $ nodeCallback , ExpressionContext::createDeep (), $ context );
12031209 $ throwPoints = $ overridingThrowPoints ?? $ condResult ->getThrowPoints ();
12041210 $ impurePoints = $ condResult ->getImpurePoints ();
1211+ $ exprType = $ scope ->getType ($ stmt ->expr );
12051212 $ scope = $ condResult ->getScope ();
12061213 $ arrayComparisonExpr = new BinaryOp \NotIdentical (
12071214 $ stmt ->expr ,
@@ -1214,6 +1221,18 @@ private function processStmtNode(
12141221 $ originalScope = $ scope ;
12151222 $ bodyScope = $ scope ;
12161223
1224+ $ isIterableAtLeastOnce = $ exprType ->isIterableAtLeastOnce ();
1225+ if (!$ context ->isTopLevel () && $ isIterableAtLeastOnce ->no ()) {
1226+ return new StatementResult (
1227+ $ scope ,
1228+ $ condResult ->hasYield (),
1229+ $ condResult ->isAlwaysTerminating (),
1230+ [],
1231+ $ throwPoints ,
1232+ $ impurePoints ,
1233+ );
1234+ }
1235+
12171236 if ($ context ->isTopLevel ()) {
12181237 $ originalScope = $ this ->polluteScopeWithAlwaysIterableForeach ? $ scope ->filterByTruthyValue ($ arrayComparisonExpr ) : $ scope ;
12191238 $ bodyScope = $ this ->enterForeach ($ originalScope , $ originalScope , $ stmt );
@@ -1250,8 +1269,6 @@ private function processStmtNode(
12501269 $ finalScope = $ breakExitPoint ->getScope ()->mergeWith ($ finalScope );
12511270 }
12521271
1253- $ exprType = $ scope ->getType ($ stmt ->expr );
1254- $ isIterableAtLeastOnce = $ exprType ->isIterableAtLeastOnce ();
12551272 if ($ exprType ->isIterable ()->no () || $ isIterableAtLeastOnce ->maybe ()) {
12561273 $ finalScope = $ finalScope ->mergeWith ($ scope ->filterByTruthyValue (new BooleanOr (
12571274 new BinaryOp \Identical (
@@ -1494,6 +1511,25 @@ private function processStmtNode(
14941511 $ bodyScope = $ condResult ->getTruthyScope ();
14951512 }
14961513
1514+ if (!$ context ->isTopLevel () && $ isIterableAtLeastOnce ->no ()) {
1515+ if (!isset ($ condResult )) {
1516+ throw new ShouldNotHappenException ();
1517+ }
1518+ if ($ this ->polluteScopeWithLoopInitialAssignments ) {
1519+ $ finalScope = $ condResult ->getFalseyScope ()->mergeWith ($ initScope );
1520+ } else {
1521+ $ finalScope = $ condResult ->getFalseyScope ()->mergeWith ($ scope );
1522+ }
1523+ return new StatementResult (
1524+ $ finalScope ,
1525+ $ hasYield ,
1526+ false ,
1527+ [],
1528+ $ throwPoints ,
1529+ $ impurePoints ,
1530+ );
1531+ }
1532+
14971533 if ($ context ->isTopLevel ()) {
14981534 $ count = 0 ;
14991535 do {
0 commit comments