@@ -2538,12 +2538,15 @@ private function createForExpr(
25382538 }
25392539 }
25402540
2541- if (!($ expr instanceof AlwaysRememberedExpr) && $ this ->expressionContainsNonPureCall ($ expr , $ scope )) {
2542- if (isset ($ containsNull ) && !$ containsNull ) {
2543- return $ this ->createNullsafeTypes ($ originalExpr , $ scope , $ context , $ type );
2544- }
2541+ if (!($ expr instanceof AlwaysRememberedExpr)) {
2542+ $ containsNonPureCall = $ this ->expressionContainsNonPureCall ($ expr , $ scope );
2543+ if ($ containsNonPureCall ->yes () || (!$ this ->rememberPossiblyImpureFunctionValues && !$ containsNonPureCall ->no ())) {
2544+ if (isset ($ containsNull ) && !$ containsNull ) {
2545+ return $ this ->createNullsafeTypes ($ originalExpr , $ scope , $ context , $ type );
2546+ }
25452547
2546- return new SpecifiedTypes ([], []);
2548+ return new SpecifiedTypes ([], []);
2549+ }
25472550 }
25482551
25492552 $ sureTypes = [];
@@ -2574,76 +2577,76 @@ private function createForExpr(
25742577 return $ types ;
25752578 }
25762579
2577- private function expressionContainsNonPureCall (Expr $ expr , Scope $ scope ): bool
2580+ private function expressionContainsNonPureCall (Expr $ expr , Scope $ scope ): TrinaryLogic
25782581 {
25792582 $ nodeFinder = new NodeFinder ();
2580- $ found = $ nodeFinder ->findFirst ([$ expr ], function (Node $ node ) use ($ scope ): bool {
2581- if (!$ node instanceof Expr) {
2582- return false ;
2583+ $ callNodes = $ nodeFinder ->find ([$ expr ], static fn (Node $ node ): bool => $ node instanceof FuncCall || $ node instanceof MethodCall || $ node instanceof StaticCall);
2584+
2585+ $ result = TrinaryLogic::createNo ();
2586+ foreach ($ callNodes as $ callNode ) {
2587+ $ result = $ result ->or ($ this ->callNodeHasSideEffects ($ callNode , $ scope ));
2588+ if ($ result ->yes ()) {
2589+ return $ result ;
25832590 }
2591+ }
25842592
2585- if ($ node instanceof FuncCall) {
2586- if ($ node ->name instanceof Name) {
2587- if (!$ this ->reflectionProvider ->hasFunction ($ node ->name , $ scope )) {
2588- return true ;
2589- }
2590- $ hasSideEffects = $ this ->reflectionProvider ->getFunction ($ node ->name , $ scope )->hasSideEffects ();
2591- return $ hasSideEffects ->yes ()
2592- || (!$ this ->rememberPossiblyImpureFunctionValues && !$ hasSideEffects ->no ());
2593- }
2593+ return $ result ;
2594+ }
25942595
2595- $ nameType = $ scope ->getType ($ node ->name );
2596- if ($ nameType ->isCallable ()->yes ()) {
2597- $ isPure = null ;
2598- foreach ($ nameType ->getCallableParametersAcceptors ($ scope ) as $ variant ) {
2599- $ variantIsPure = $ variant ->isPure ();
2600- $ isPure = $ isPure === null ? $ variantIsPure : $ isPure ->and ($ variantIsPure );
2601- }
2602- if ($ isPure !== null ) {
2603- return $ isPure ->no ()
2604- || (!$ this ->rememberPossiblyImpureFunctionValues && !$ isPure ->yes ());
2605- }
2596+ private function callNodeHasSideEffects (Node $ node , Scope $ scope ): TrinaryLogic
2597+ {
2598+ if ($ node instanceof FuncCall) {
2599+ if ($ node ->name instanceof Name) {
2600+ if (!$ this ->reflectionProvider ->hasFunction ($ node ->name , $ scope )) {
2601+ return TrinaryLogic::createYes ();
26062602 }
2607-
2608- return false ;
2603+ return $ this ->reflectionProvider ->getFunction ($ node ->name , $ scope )->hasSideEffects ();
26092604 }
26102605
2611- if ($ node instanceof MethodCall) {
2612- if ($ node ->name instanceof Identifier) {
2613- $ calledOnType = $ scope ->getType ($ node ->var );
2614- $ methodReflection = $ scope ->getMethodReflection ($ calledOnType , $ node ->name ->name );
2615- if ($ methodReflection === null ) {
2616- return true ;
2617- }
2618- $ hasSideEffects = $ methodReflection ->hasSideEffects ();
2619- return $ hasSideEffects ->yes ()
2620- || (!$ this ->rememberPossiblyImpureFunctionValues && !$ hasSideEffects ->no ());
2606+ $ nameType = $ scope ->getType ($ node ->name );
2607+ if ($ nameType ->isCallable ()->yes ()) {
2608+ $ isPure = null ;
2609+ foreach ($ nameType ->getCallableParametersAcceptors ($ scope ) as $ variant ) {
2610+ $ variantIsPure = $ variant ->isPure ();
2611+ $ isPure = $ isPure === null ? $ variantIsPure : $ isPure ->and ($ variantIsPure );
2612+ }
2613+ if ($ isPure !== null ) {
2614+ return $ isPure ->negate ();
26212615 }
2622- return true ;
26232616 }
26242617
2625- if ($ node instanceof StaticCall) {
2626- if ($ node ->name instanceof Identifier) {
2627- if ($ node ->class instanceof Name) {
2628- $ calledOnType = $ scope ->resolveTypeByName ($ node ->class );
2629- } else {
2630- $ calledOnType = $ scope ->getType ($ node ->class );
2631- }
2632- $ methodReflection = $ scope ->getMethodReflection ($ calledOnType , $ node ->name ->name );
2633- if ($ methodReflection === null ) {
2634- return true ;
2635- }
2636- $ hasSideEffects = $ methodReflection ->hasSideEffects ();
2637- return $ hasSideEffects ->yes ()
2638- || (!$ this ->rememberPossiblyImpureFunctionValues && !$ hasSideEffects ->no ());
2618+ return TrinaryLogic::createNo ();
2619+ }
2620+
2621+ if ($ node instanceof MethodCall) {
2622+ if ($ node ->name instanceof Identifier) {
2623+ $ calledOnType = $ scope ->getType ($ node ->var );
2624+ $ methodReflection = $ scope ->getMethodReflection ($ calledOnType , $ node ->name ->name );
2625+ if ($ methodReflection === null ) {
2626+ return TrinaryLogic::createMaybe ();
26392627 }
2640- return true ;
2628+ return $ methodReflection -> hasSideEffects () ;
26412629 }
2630+ return TrinaryLogic::createMaybe ();
2631+ }
26422632
2643- return false ;
2644- });
2633+ if ($ node instanceof StaticCall) {
2634+ if ($ node ->name instanceof Identifier) {
2635+ if ($ node ->class instanceof Name) {
2636+ $ calledOnType = $ scope ->resolveTypeByName ($ node ->class );
2637+ } else {
2638+ $ calledOnType = $ scope ->getType ($ node ->class );
2639+ }
2640+ $ methodReflection = $ scope ->getMethodReflection ($ calledOnType , $ node ->name ->name );
2641+ if ($ methodReflection === null ) {
2642+ return TrinaryLogic::createMaybe ();
2643+ }
2644+ return $ methodReflection ->hasSideEffects ();
2645+ }
2646+ return TrinaryLogic::createMaybe ();
2647+ }
26452648
2646- return $ found !== null ;
2649+ return TrinaryLogic:: createNo () ;
26472650 }
26482651
26492652 private function createNullsafeTypes (Expr $ expr , Scope $ scope , TypeSpecifierContext $ context , ?Type $ type ): SpecifiedTypes
0 commit comments