@@ -2415,13 +2415,13 @@ public function processExprNode(Node\Stmt $stmt, Expr $expr, MutatingScope $scop
24152415return $ this ->processExprNode ($ stmt , $ newExpr , $ scope , $ nodeCallback , $ context );
24162416}
24172417
2418- $ isAlwaysTerminating = false ;
24192418$ this ->callNodeCallbackWithExpression ($ nodeCallback , $ expr , $ scope , $ context );
24202419
24212420if ($ expr instanceof Variable) {
24222421$ hasYield = false ;
24232422$ throwPoints = [];
24242423$ impurePoints = [];
2424+ $ isAlwaysTerminating = false ;
24252425if ($ expr ->name instanceof Expr) {
24262426return $ this ->processExprNode ($ stmt , $ expr ->name , $ scope , $ nodeCallback , $ context ->enterDeep ());
24272427} elseif (in_array ($ expr ->name , Scope::SUPERGLOBAL_VARIABLES , true )) {
@@ -2539,6 +2539,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context): Exp
25392539$ functionReflection = null ;
25402540$ throwPoints = [];
25412541$ impurePoints = [];
2542+ $ isAlwaysTerminating = false ;
25422543if ($ expr ->name instanceof Expr) {
25432544$ nameType = $ scope ->getType ($ expr ->name );
25442545if (!$ nameType ->isCallable ()->no ()) {
@@ -2943,6 +2944,7 @@ static function (): void {
29432944$ hasYield = false ;
29442945$ throwPoints = [];
29452946$ impurePoints = [];
2947+ $ isAlwaysTerminating = false ;
29462948if ($ expr ->class instanceof Expr) {
29472949$ objectClasses = $ scope ->getType ($ expr ->class )->getObjectClassNames ();
29482950if (count ($ objectClasses ) !== 1 ) {
@@ -3106,6 +3108,7 @@ static function (): void {
31063108$ hasYield = $ result ->hasYield ();
31073109$ throwPoints = $ result ->getThrowPoints ();
31083110$ impurePoints = $ result ->getImpurePoints ();
3111+ $ isAlwaysTerminating = false ;
31093112$ scope = $ result ->getScope ();
31103113if ($ expr ->name instanceof Expr) {
31113114$ result = $ this ->processExprNode ($ stmt , $ expr ->name , $ scope , $ nodeCallback , $ context ->enterDeep ());
@@ -3154,6 +3157,7 @@ static function (): void {
31543157true ,
31553158),
31563159];
3160+ $ isAlwaysTerminating = false ;
31573161if ($ expr ->class instanceof Expr) {
31583162$ result = $ this ->processExprNode ($ stmt , $ expr ->class , $ scope , $ nodeCallback , $ context ->enterDeep ());
31593163$ hasYield = $ result ->hasYield ();
@@ -3192,6 +3196,7 @@ static function (): void {
31923196$ hasYield = $ result ->hasYield ();
31933197$ throwPoints = $ result ->getThrowPoints ();
31943198$ impurePoints = $ result ->getImpurePoints ();
3199+ $ isAlwaysTerminating = false ;
31953200$ scope = $ result ->getScope ();
31963201} elseif ($ expr instanceof Exit_) {
31973202$ hasYield = false ;
@@ -3213,6 +3218,7 @@ static function (): void {
32133218$ hasYield = false ;
32143219$ throwPoints = [];
32153220$ impurePoints = [];
3221+ $ isAlwaysTerminating = false ;
32163222foreach ($ expr ->parts as $ part ) {
32173223if (!$ part instanceof Expr) {
32183224continue ;
@@ -3228,6 +3234,7 @@ static function (): void {
32283234$ hasYield = false ;
32293235$ throwPoints = [];
32303236$ impurePoints = [];
3237+ $ isAlwaysTerminating = false ;
32313238if ($ expr ->dim !== null ) {
32323239$ result = $ this ->processExprNode ($ stmt , $ expr ->dim , $ scope , $ nodeCallback , $ context ->enterDeep ());
32333240$ hasYield = $ result ->hasYield ();
@@ -3246,6 +3253,7 @@ static function (): void {
32463253$ hasYield = false ;
32473254$ throwPoints = [];
32483255$ impurePoints = [];
3256+ $ isAlwaysTerminating = false ;
32493257foreach ($ expr ->items as $ arrayItem ) {
32503258$ itemNodes [] = new LiteralArrayItem ($ scope , $ arrayItem );
32513259$ nodeCallback ($ arrayItem , $ scope );
@@ -3327,6 +3335,7 @@ static function (): void {
33273335$ hasYield = $ condResult ->hasYield () || $ rightResult ->hasYield ();
33283336$ throwPoints = array_merge ($ condResult ->getThrowPoints (), $ rightResult ->getThrowPoints ());
33293337$ impurePoints = array_merge ($ condResult ->getImpurePoints (), $ rightResult ->getImpurePoints ());
3338+ $ isAlwaysTerminating = false ;
33303339} elseif ($ expr instanceof BinaryOp) {
33313340$ result = $ this ->processExprNode ($ stmt , $ expr ->left , $ scope , $ nodeCallback , $ context ->enterDeep ());
33323341$ scope = $ result ->getScope ();
@@ -3359,11 +3368,13 @@ static function (): void {
33593368true ,
33603369);
33613370$ hasYield = $ result ->hasYield ();
3371+ $ isAlwaysTerminating = false ;
33623372$ scope = $ result ->getScope ()->afterExtractCall ();
33633373} elseif ($ expr instanceof Expr \Print_) {
33643374$ result = $ this ->processExprNode ($ stmt , $ expr ->expr , $ scope , $ nodeCallback , $ context ->enterDeep ());
33653375$ throwPoints = $ result ->getThrowPoints ();
33663376$ impurePoints = $ result ->getImpurePoints ();
3377+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
33673378$ impurePoints [] = new ImpurePoint ($ scope , $ expr , 'print ' , 'print ' , true );
33683379$ hasYield = $ result ->hasYield ();
33693380
@@ -3372,6 +3383,7 @@ static function (): void {
33723383$ result = $ this ->processExprNode ($ stmt , $ expr ->expr , $ scope , $ nodeCallback , $ context ->enterDeep ());
33733384$ throwPoints = $ result ->getThrowPoints ();
33743385$ impurePoints = $ result ->getImpurePoints ();
3386+ $ isAlwaysTerminating = true ;
33753387$ hasYield = $ result ->hasYield ();
33763388
33773389$ exprType = $ scope ->getType ($ expr ->expr );
@@ -3399,6 +3411,7 @@ static function (): void {
33993411$ result = $ this ->processExprNode ($ stmt , $ expr ->expr , $ scope , $ nodeCallback , $ context ->enterDeep ());
34003412$ throwPoints = $ result ->getThrowPoints ();
34013413$ impurePoints = $ result ->getImpurePoints ();
3414+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
34023415$ hasYield = $ result ->hasYield ();
34033416
34043417$ scope = $ result ->getScope ();
@@ -3409,6 +3422,7 @@ static function (): void {
34093422$ impurePoints = $ result ->getImpurePoints ();
34103423$ impurePoints [] = new ImpurePoint ($ scope , $ expr , 'eval ' , 'eval ' , true );
34113424$ hasYield = $ result ->hasYield ();
3425+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
34123426
34133427$ scope = $ result ->getScope ();
34143428} elseif ($ expr instanceof Expr \YieldFrom) {
@@ -3424,6 +3438,7 @@ static function (): void {
34243438true ,
34253439);
34263440$ hasYield = true ;
3441+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
34273442
34283443$ scope = $ result ->getScope ();
34293444} elseif ($ expr instanceof BooleanNot) {
@@ -3432,7 +3447,10 @@ static function (): void {
34323447$ hasYield = $ result ->hasYield ();
34333448$ throwPoints = $ result ->getThrowPoints ();
34343449$ impurePoints = $ result ->getImpurePoints ();
3450+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
34353451} elseif ($ expr instanceof Expr \ClassConstFetch) {
3452+ $ isAlwaysTerminating = false ;
3453+
34363454if ($ expr ->class instanceof Expr) {
34373455$ result = $ this ->processExprNode ($ stmt , $ expr ->class , $ scope , $ nodeCallback , $ context ->enterDeep ());
34383456$ scope = $ result ->getScope ();
@@ -3463,13 +3481,15 @@ static function (): void {
34633481$ hasYield = $ result ->hasYield ();
34643482$ throwPoints = $ result ->getThrowPoints ();
34653483$ impurePoints = $ result ->getImpurePoints ();
3484+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
34663485$ scope = $ this ->revertNonNullability ($ scope , $ nonNullabilityResult ->getSpecifiedExpressions ());
34673486$ scope = $ this ->lookForUnsetAllowedUndefinedExpressions ($ scope , $ expr ->expr );
34683487} elseif ($ expr instanceof Expr \Isset_) {
34693488$ hasYield = false ;
34703489$ throwPoints = [];
34713490$ impurePoints = [];
34723491$ nonNullabilityResults = [];
3492+ $ isAlwaysTerminating = false ;
34733493foreach ($ expr ->vars as $ var ) {
34743494$ nonNullabilityResult = $ this ->ensureNonNullability ($ scope , $ var );
34753495$ scope = $ this ->lookForSetAllowedUndefinedExpressions ($ nonNullabilityResult ->getScope (), $ var );
@@ -3478,6 +3498,7 @@ static function (): void {
34783498$ hasYield = $ hasYield || $ result ->hasYield ();
34793499$ throwPoints = array_merge ($ throwPoints , $ result ->getThrowPoints ());
34803500$ impurePoints = array_merge ($ impurePoints , $ result ->getImpurePoints ());
3501+ $ isAlwaysTerminating = $ isAlwaysTerminating || $ result ->isAlwaysTerminating ();
34813502$ nonNullabilityResults [] = $ nonNullabilityResult ;
34823503}
34833504foreach (array_reverse ($ expr ->vars ) as $ var ) {
@@ -3492,6 +3513,7 @@ static function (): void {
34923513$ hasYield = $ result ->hasYield ();
34933514$ throwPoints = $ result ->getThrowPoints ();
34943515$ impurePoints = $ result ->getImpurePoints ();
3516+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
34953517if ($ expr ->class instanceof Expr) {
34963518$ result = $ this ->processExprNode ($ stmt , $ expr ->class , $ scope , $ nodeCallback , $ context ->enterDeep ());
34973519$ scope = $ result ->getScope ();
@@ -3508,6 +3530,7 @@ static function (): void {
35083530$ hasYield = false ;
35093531$ throwPoints = [];
35103532$ impurePoints = [];
3533+ $ isAlwaysTerminating = false ;
35113534$ className = null ;
35123535if ($ expr ->class instanceof Expr || $ expr ->class instanceof Name) {
35133536if ($ expr ->class instanceof Expr) {
@@ -3632,6 +3655,7 @@ static function (): void {
36323655$ hasYield = $ result ->hasYield ();
36333656$ throwPoints = $ result ->getThrowPoints ();
36343657$ impurePoints = $ result ->getImpurePoints ();
3658+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
36353659
36363660$ newExpr = $ expr ;
36373661if ($ expr instanceof Expr \PostInc) {
@@ -3660,20 +3684,23 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
36603684$ ternaryCondResult = $ this ->processExprNode ($ stmt , $ expr ->cond , $ scope , $ nodeCallback , $ context ->enterDeep ());
36613685$ throwPoints = $ ternaryCondResult ->getThrowPoints ();
36623686$ impurePoints = $ ternaryCondResult ->getImpurePoints ();
3687+ $ isAlwaysTerminating = $ ternaryCondResult ->isAlwaysTerminating ();
36633688$ ifTrueScope = $ ternaryCondResult ->getTruthyScope ();
36643689$ ifFalseScope = $ ternaryCondResult ->getFalseyScope ();
36653690$ ifTrueType = null ;
36663691if ($ expr ->if !== null ) {
36673692$ ifResult = $ this ->processExprNode ($ stmt , $ expr ->if , $ ifTrueScope , $ nodeCallback , $ context );
36683693$ throwPoints = array_merge ($ throwPoints , $ ifResult ->getThrowPoints ());
36693694$ impurePoints = array_merge ($ impurePoints , $ ifResult ->getImpurePoints ());
3695+ $ isAlwaysTerminating = $ isAlwaysTerminating || $ ifResult ->isAlwaysTerminating ();
36703696$ ifTrueScope = $ ifResult ->getScope ();
36713697$ ifTrueType = $ ifTrueScope ->getType ($ expr ->if );
36723698}
36733699
36743700$ elseResult = $ this ->processExprNode ($ stmt , $ expr ->else , $ ifFalseScope , $ nodeCallback , $ context );
36753701$ throwPoints = array_merge ($ throwPoints , $ elseResult ->getThrowPoints ());
36763702$ impurePoints = array_merge ($ impurePoints , $ elseResult ->getImpurePoints ());
3703+ $ isAlwaysTerminating = $ isAlwaysTerminating || $ elseResult ->isAlwaysTerminating ();
36773704$ ifFalseScope = $ elseResult ->getScope ();
36783705
36793706$ condType = $ scope ->getType ($ expr ->cond );
@@ -3698,7 +3725,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
36983725return new ExpressionResult (
36993726$ finalScope ,
37003727$ ternaryCondResult ->hasYield (),
3701- false ,
3728+ $ isAlwaysTerminating ,
37023729$ throwPoints ,
37033730$ impurePoints ,
37043731static fn (): MutatingScope => $ finalScope ->filterByTruthyValue ($ expr ),
@@ -3718,17 +3745,20 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
37183745true ,
37193746),
37203747];
3748+ $ isAlwaysTerminating = false ;
37213749if ($ expr ->key !== null ) {
37223750$ keyResult = $ this ->processExprNode ($ stmt , $ expr ->key , $ scope , $ nodeCallback , $ context ->enterDeep ());
37233751$ scope = $ keyResult ->getScope ();
37243752$ throwPoints = $ keyResult ->getThrowPoints ();
37253753$ impurePoints = array_merge ($ impurePoints , $ keyResult ->getImpurePoints ());
3754+ $ isAlwaysTerminating = $ isAlwaysTerminating || $ keyResult ->isAlwaysTerminating ();
37263755}
37273756if ($ expr ->value !== null ) {
37283757$ valueResult = $ this ->processExprNode ($ stmt , $ expr ->value , $ scope , $ nodeCallback , $ context ->enterDeep ());
37293758$ scope = $ valueResult ->getScope ();
37303759$ throwPoints = array_merge ($ throwPoints , $ valueResult ->getThrowPoints ());
37313760$ impurePoints = array_merge ($ impurePoints , $ valueResult ->getImpurePoints ());
3761+ $ isAlwaysTerminating = $ isAlwaysTerminating || $ valueResult ->isAlwaysTerminating ();
37323762}
37333763$ hasYield = true ;
37343764} elseif ($ expr instanceof Expr \Match_) {
@@ -3739,6 +3769,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
37393769$ hasYield = $ condResult ->hasYield ();
37403770$ throwPoints = $ condResult ->getThrowPoints ();
37413771$ impurePoints = $ condResult ->getImpurePoints ();
3772+ $ isAlwaysTerminating = $ condResult ->isAlwaysTerminating ();
37423773$ matchScope = $ scope ->enterMatch ($ expr );
37433774$ armNodes = [];
37443775$ hasDefaultCond = false ;
@@ -3950,79 +3981,93 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
39503981$ hasYield = $ result ->hasYield ();
39513982$ throwPoints = $ result ->getThrowPoints ();
39523983$ impurePoints = $ result ->getImpurePoints ();
3984+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
39533985$ scope = $ result ->getScope ();
39543986} elseif ($ expr instanceof Expr \Throw_) {
39553987$ hasYield = false ;
39563988$ result = $ this ->processExprNode ($ stmt , $ expr ->expr , $ scope , $ nodeCallback , ExpressionContext::createDeep ());
39573989$ throwPoints = $ result ->getThrowPoints ();
39583990$ impurePoints = $ result ->getImpurePoints ();
3991+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
39593992$ throwPoints [] = ThrowPoint::createExplicit ($ scope , $ scope ->getType ($ expr ->expr ), $ expr , false );
39603993} elseif ($ expr instanceof FunctionCallableNode) {
39613994$ throwPoints = [];
39623995$ impurePoints = [];
39633996$ hasYield = false ;
3997+ $ isAlwaysTerminating = false ;
39643998if ($ expr ->getName () instanceof Expr) {
39653999$ result = $ this ->processExprNode ($ stmt , $ expr ->getName (), $ scope , $ nodeCallback , ExpressionContext::createDeep ());
39664000$ scope = $ result ->getScope ();
39674001$ hasYield = $ result ->hasYield ();
39684002$ throwPoints = $ result ->getThrowPoints ();
39694003$ impurePoints = $ result ->getImpurePoints ();
4004+ $ isAlwaysTerminating = $ result ->isAlwaysTerminating ();
39704005}
39714006} elseif ($ expr instanceof MethodCallableNode) {
39724007$ result = $ this ->processExprNode ($ stmt , $ expr ->getVar (), $ scope , $ nodeCallback , ExpressionContext::createDeep ());
39734008$ scope = $ result ->getScope ();
39744009$ hasYield = $ result ->hasYield ();
39754010$ throwPoints = $ result ->getThrowPoints ();
39764011$ impurePoints = $ result ->getImpurePoints ();
4012+ $ isAlwaysTerminating = false ;
39774013if ($ expr ->getName () instanceof Expr) {
39784014$ nameResult = $ this ->processExprNode ($ stmt , $ expr ->getVar (), $ scope , $ nodeCallback , ExpressionContext::createDeep ());
39794015$ scope = $ nameResult ->getScope ();
39804016$ hasYield = $ hasYield || $ nameResult ->hasYield ();
39814017$ throwPoints = array_merge ($ throwPoints , $ nameResult ->getThrowPoints ());
39824018$ impurePoints = array_merge ($ impurePoints , $ nameResult ->getImpurePoints ());
4019+ $ isAlwaysTerminating = $ nameResult ->isAlwaysTerminating ();
39834020}
39844021} elseif ($ expr instanceof StaticMethodCallableNode) {
39854022$ throwPoints = [];
39864023$ impurePoints = [];
39874024$ hasYield = false ;
4025+ $ isAlwaysTerminating = false ;
39884026if ($ expr ->getClass () instanceof Expr) {
39894027$ classResult = $ this ->processExprNode ($ stmt , $ expr ->getClass (), $ scope , $ nodeCallback , ExpressionContext::createDeep ());
39904028$ scope = $ classResult ->getScope ();
39914029$ hasYield = $ classResult ->hasYield ();
39924030$ throwPoints = $ classResult ->getThrowPoints ();
39934031$ impurePoints = $ classResult ->getImpurePoints ();
4032+ $ isAlwaysTerminating = $ isAlwaysTerminating || $ classResult ->isAlwaysTerminating ();
39944033}
39954034if ($ expr ->getName () instanceof Expr) {
39964035$ nameResult = $ this ->processExprNode ($ stmt , $ expr ->getName (), $ scope , $ nodeCallback , ExpressionContext::createDeep ());
39974036$ scope = $ nameResult ->getScope ();
39984037$ hasYield = $ hasYield || $ nameResult ->hasYield ();
39994038$ throwPoints = array_merge ($ throwPoints , $ nameResult ->getThrowPoints ());
40004039$ impurePoints = array_merge ($ impurePoints , $ nameResult ->getImpurePoints ());
4040+ $ isAlwaysTerminating = $ isAlwaysTerminating || $ nameResult ->isAlwaysTerminating ();
40014041}
40024042} elseif ($ expr instanceof InstantiationCallableNode) {
40034043$ throwPoints = [];
40044044$ impurePoints = [];
40054045$ hasYield = false ;
4046+ $ isAlwaysTerminating = false ;
40064047if ($ expr ->getClass () instanceof Expr) {
40074048$ classResult = $ this ->processExprNode ($ stmt , $ expr ->getClass (), $ scope , $ nodeCallback , ExpressionContext::createDeep ());
40084049$ scope = $ classResult ->getScope ();
40094050$ hasYield = $ classResult ->hasYield ();
40104051$ throwPoints = $ classResult ->getThrowPoints ();
40114052$ impurePoints = $ classResult ->getImpurePoints ();
4053+ $ isAlwaysTerminating = $ isAlwaysTerminating || $ classResult ->isAlwaysTerminating ();
40124054}
40134055} elseif ($ expr instanceof Node \Scalar) {
40144056$ hasYield = false ;
40154057$ throwPoints = [];
40164058$ impurePoints = [];
4059+ $ isAlwaysTerminating = false ;
40174060} elseif ($ expr instanceof ConstFetch) {
40184061$ hasYield = false ;
40194062$ throwPoints = [];
40204063$ impurePoints = [];
4064+ $ isAlwaysTerminating = false ;
40214065$ nodeCallback ($ expr ->name , $ scope );
40224066} else {
40234067$ hasYield = false ;
40244068$ throwPoints = [];
40254069$ impurePoints = [];
4070+ $ isAlwaysTerminating = false ;
40264071}
40274072
40284073return new ExpressionResult (
0 commit comments