@@ -735,6 +735,18 @@ public function specifyTypesInCondition(
735735 $ rightTypesForHolders = $ this ->specifyTypesInCondition ($ rightScope , $ expr ->right , TypeSpecifierContext::createFalsey ())->setRootExpr ($ expr );
736736 }
737737 }
738+ if ($ leftTypesForHolders ->getSureTypes () === [] && $ leftTypesForHolders ->getSureNotTypes () === []) {
739+ $ truthyLeftTypes = $ this ->specifyTypesInCondition ($ scope , $ expr ->left , TypeSpecifierContext::createTruthy ());
740+ if ($ this ->allExpressionsTrackable ($ truthyLeftTypes )) {
741+ $ leftTypesForHolders = new SpecifiedTypes ($ truthyLeftTypes ->getSureNotTypes (), $ truthyLeftTypes ->getSureTypes ());
742+ }
743+ }
744+ if ($ rightTypesForHolders ->getSureTypes () === [] && $ rightTypesForHolders ->getSureNotTypes () === []) {
745+ $ truthyRightTypes = $ this ->specifyTypesInCondition ($ rightScope , $ expr ->right , TypeSpecifierContext::createTruthy ());
746+ if ($ this ->allExpressionsTrackable ($ truthyRightTypes )) {
747+ $ rightTypesForHolders = new SpecifiedTypes ($ truthyRightTypes ->getSureNotTypes (), $ truthyRightTypes ->getSureTypes ());
748+ }
749+ }
738750 $ result = new SpecifiedTypes (
739751 $ types ->getSureTypes (),
740752 $ types ->getSureNotTypes (),
@@ -743,10 +755,14 @@ public function specifyTypesInCondition(
743755 $ result = $ result ->setAlwaysOverwriteTypes ();
744756 }
745757 return $ result ->setNewConditionalExpressionHolders (array_merge (
746- $ this ->processBooleanNotSureConditionalTypes ($ scope , $ leftTypesForHolders , $ rightTypesForHolders , $ rightScope ),
747- $ this ->processBooleanNotSureConditionalTypes ($ scope , $ rightTypesForHolders , $ leftTypesForHolders , $ scope ),
748- $ this ->processBooleanSureConditionalTypes ($ scope , $ leftTypesForHolders , $ rightTypesForHolders , $ rightScope ),
749- $ this ->processBooleanSureConditionalTypes ($ scope , $ rightTypesForHolders , $ leftTypesForHolders , $ scope ),
758+ $ this ->processBooleanConditionalTypes ($ scope , $ leftTypesForHolders , false , $ rightTypesForHolders , false , $ rightScope ),
759+ $ this ->processBooleanConditionalTypes ($ scope , $ rightTypesForHolders , false , $ leftTypesForHolders , false , $ scope ),
760+ $ this ->processBooleanConditionalTypes ($ scope , $ leftTypesForHolders , true , $ rightTypesForHolders , true , $ rightScope ),
761+ $ this ->processBooleanConditionalTypes ($ scope , $ rightTypesForHolders , true , $ leftTypesForHolders , true , $ scope ),
762+ $ this ->processBooleanConditionalTypes ($ scope , $ leftTypesForHolders , false , $ rightTypesForHolders , true , $ rightScope ),
763+ $ this ->processBooleanConditionalTypes ($ scope , $ rightTypesForHolders , false , $ leftTypesForHolders , true , $ scope ),
764+ $ this ->processBooleanConditionalTypes ($ scope , $ leftTypesForHolders , true , $ rightTypesForHolders , false , $ rightScope ),
765+ $ this ->processBooleanConditionalTypes ($ scope , $ rightTypesForHolders , true , $ leftTypesForHolders , false , $ scope ),
750766 ))->setRootExpr ($ expr );
751767 }
752768
@@ -796,10 +812,14 @@ public function specifyTypesInCondition(
796812 $ result = $ result ->setAlwaysOverwriteTypes ();
797813 }
798814 return $ result ->setNewConditionalExpressionHolders (array_merge (
799- $ this ->processBooleanNotSureConditionalTypes ($ scope , $ leftTypes , $ rightTypes , $ rightScope ),
800- $ this ->processBooleanNotSureConditionalTypes ($ scope , $ rightTypes , $ leftTypes , $ scope ),
801- $ this ->processBooleanSureConditionalTypes ($ scope , $ leftTypes , $ rightTypes , $ rightScope ),
802- $ this ->processBooleanSureConditionalTypes ($ scope , $ rightTypes , $ leftTypes , $ scope ),
815+ $ this ->processBooleanConditionalTypes ($ scope , $ leftTypes , false , $ rightTypes , false , $ rightScope ),
816+ $ this ->processBooleanConditionalTypes ($ scope , $ rightTypes , false , $ leftTypes , false , $ scope ),
817+ $ this ->processBooleanConditionalTypes ($ scope , $ leftTypes , true , $ rightTypes , true , $ rightScope ),
818+ $ this ->processBooleanConditionalTypes ($ scope , $ rightTypes , true , $ leftTypes , true , $ scope ),
819+ $ this ->processBooleanConditionalTypes ($ scope , $ leftTypes , false , $ rightTypes , true , $ rightScope ),
820+ $ this ->processBooleanConditionalTypes ($ scope , $ rightTypes , false , $ leftTypes , true , $ scope ),
821+ $ this ->processBooleanConditionalTypes ($ scope , $ leftTypes , true , $ rightTypes , false , $ rightScope ),
822+ $ this ->processBooleanConditionalTypes ($ scope , $ rightTypes , true , $ leftTypes , false , $ scope ),
803823 ))->setRootExpr ($ expr );
804824 }
805825
@@ -2066,18 +2086,25 @@ private function augmentDisjunctionTypes(
20662086 /**
20672087 * @return array<string, ConditionalExpressionHolder[]>
20682088 */
2069- private function processBooleanSureConditionalTypes (Scope $ scope , SpecifiedTypes $ leftTypes , SpecifiedTypes $ rightTypes , Scope $ rightScope ): array
2089+ private function processBooleanConditionalTypes (Scope $ scope , SpecifiedTypes $ conditionSpecifiedTypes , bool $ conditionsFromSureTypes , SpecifiedTypes $ holderSpecifiedTypes , bool $ holdersFromSureTypes , Scope $ rightScope ): array
20702090 {
2091+ $ conditionTypes = $ conditionsFromSureTypes ? $ conditionSpecifiedTypes ->getSureTypes () : $ conditionSpecifiedTypes ->getSureNotTypes ();
2092+ $ holderTypes = $ holdersFromSureTypes ? $ holderSpecifiedTypes ->getSureTypes () : $ holderSpecifiedTypes ->getSureNotTypes ();
2093+
20712094 $ conditionExpressionTypes = [];
2072- foreach ($ leftTypes -> getSureTypes () as $ exprString => [$ expr , $ type ]) {
2095+ foreach ($ conditionTypes as $ exprString => [$ expr , $ type ]) {
20732096 if (!$ this ->isTrackableExpression ($ expr )) {
20742097 continue ;
20752098 }
20762099
2077- $ scopeType = $ scope ->getType ($ expr );
2078- $ conditionType = TypeCombinator::remove ($ scopeType , $ type );
2079- if ($ scopeType ->equals ($ conditionType )) {
2080- continue ;
2100+ if ($ conditionsFromSureTypes ) {
2101+ $ scopeType = $ scope ->getType ($ expr );
2102+ $ conditionType = TypeCombinator::remove ($ scopeType , $ type );
2103+ if ($ scopeType ->equals ($ conditionType )) {
2104+ continue ;
2105+ }
2106+ } else {
2107+ $ conditionType = TypeCombinator::intersect ($ scope ->getType ($ expr ), $ type );
20812108 }
20822109
20832110 $ conditionExpressionTypes [$ exprString ] = ExpressionTypeHolder::createYes (
@@ -2088,7 +2115,7 @@ private function processBooleanSureConditionalTypes(Scope $scope, SpecifiedTypes
20882115
20892116 if (count ($ conditionExpressionTypes ) > 0 ) {
20902117 $ holders = [];
2091- foreach ($ rightTypes -> getSureTypes () as $ exprString => [$ expr , $ type ]) {
2118+ foreach ($ holderTypes as $ exprString => [$ expr , $ type ]) {
20922119 if (!$ this ->isTrackableExpression ($ expr )) {
20932120 continue ;
20942121 }
@@ -2110,9 +2137,12 @@ private function processBooleanSureConditionalTypes(Scope $scope, SpecifiedTypes
21102137 }
21112138
21122139 $ targetScope = $ expr instanceof Expr \Variable ? $ scope : $ rightScope ;
2140+ $ holderType = $ holdersFromSureTypes
2141+ ? TypeCombinator::intersect ($ targetScope ->getType ($ expr ), $ type )
2142+ : TypeCombinator::remove ($ targetScope ->getType ($ expr ), $ type );
21132143 $ holder = new ConditionalExpressionHolder (
21142144 $ conditions ,
2115- ExpressionTypeHolder::createYes ($ expr , TypeCombinator:: intersect ( $ targetScope -> getType ( $ expr ), $ type ) ),
2145+ ExpressionTypeHolder::createYes ($ expr , $ holderType ),
21162146 );
21172147 $ holders [$ exprString ][$ holder ->getKey ()] = $ holder ;
21182148 }
@@ -2134,6 +2164,22 @@ private function isTrackableExpression(Expr $expr): bool
21342164 || $ expr instanceof Expr \StaticPropertyFetch;
21352165 }
21362166
2167+ private function allExpressionsTrackable (SpecifiedTypes $ types ): bool
2168+ {
2169+ foreach ($ types ->getSureTypes () as [$ expr ]) {
2170+ if (!$ this ->isTrackableExpression ($ expr )) {
2171+ return false ;
2172+ }
2173+ }
2174+ foreach ($ types ->getSureNotTypes () as [$ expr ]) {
2175+ if (!$ this ->isTrackableExpression ($ expr )) {
2176+ return false ;
2177+ }
2178+ }
2179+
2180+ return $ types ->getSureTypes () !== [] || $ types ->getSureNotTypes () !== [];
2181+ }
2182+
21372183 /**
21382184 * Flatten a deep BooleanOr chain into leaf expressions and process them
21392185 * without recursive filterByFalseyValue calls. This reduces O(n^2) to O(n)
@@ -2261,60 +2307,6 @@ private function specifyTypesForFlattenedBooleanAnd(
22612307 return (new SpecifiedTypes ($ sureTypes , $ sureNotTypes ))->setRootExpr ($ expr );
22622308 }
22632309
2264- /**
2265- * @return array<string, ConditionalExpressionHolder[]>
2266- */
2267- private function processBooleanNotSureConditionalTypes (Scope $ scope , SpecifiedTypes $ leftTypes , SpecifiedTypes $ rightTypes , Scope $ rightScope ): array
2268- {
2269- $ conditionExpressionTypes = [];
2270- foreach ($ leftTypes ->getSureNotTypes () as $ exprString => [$ expr , $ type ]) {
2271- if (!$ this ->isTrackableExpression ($ expr )) {
2272- continue ;
2273- }
2274-
2275- $ conditionExpressionTypes [$ exprString ] = ExpressionTypeHolder::createYes (
2276- $ expr ,
2277- TypeCombinator::intersect ($ scope ->getType ($ expr ), $ type ),
2278- );
2279- }
2280-
2281- if (count ($ conditionExpressionTypes ) > 0 ) {
2282- $ holders = [];
2283- foreach ($ rightTypes ->getSureNotTypes () as $ exprString => [$ expr , $ type ]) {
2284- if (!$ this ->isTrackableExpression ($ expr )) {
2285- continue ;
2286- }
2287-
2288- if (!isset ($ holders [$ exprString ])) {
2289- $ holders [$ exprString ] = [];
2290- }
2291-
2292- $ conditions = $ conditionExpressionTypes ;
2293- foreach (array_keys ($ conditions ) as $ conditionExprString ) {
2294- if ($ conditionExprString !== $ exprString ) {
2295- continue ;
2296- }
2297- unset($ conditions [$ conditionExprString ]);
2298- }
2299-
2300- if (count ($ conditions ) === 0 ) {
2301- continue ;
2302- }
2303-
2304- $ targetScope = $ expr instanceof Expr \Variable ? $ scope : $ rightScope ;
2305- $ holder = new ConditionalExpressionHolder (
2306- $ conditions ,
2307- ExpressionTypeHolder::createYes ($ expr , TypeCombinator::remove ($ targetScope ->getType ($ expr ), $ type )),
2308- );
2309- $ holders [$ exprString ][$ holder ->getKey ()] = $ holder ;
2310- }
2311-
2312- return $ holders ;
2313- }
2314-
2315- return [];
2316- }
2317-
23182310 /**
23192311 * @return array{Expr, ConstantScalarType, Type}|null
23202312 */
0 commit comments