@@ -1759,14 +1759,22 @@ private function ensureShallowNonNullability(MutatingScope $scope, Scope $origin
17591759 if ($ isNull ->yes ()) {
17601760 return new EnsuredNonNullabilityResult ($ scope , []);
17611761 }
1762+
1763+ // keep certainty
1764+ $ certainty = TrinaryLogic::createYes ();
1765+ $ hasExpressionType = $ originalScope ->hasExpressionType ($ exprToSpecify );
1766+ if (!$ hasExpressionType ->no ()) {
1767+ $ certainty = $ hasExpressionType ;
1768+ }
1769+
17621770 $ exprTypeWithoutNull = TypeCombinator::removeNull ($ exprType );
17631771 if ($ exprType ->equals ($ exprTypeWithoutNull )) {
17641772 $ originalExprType = $ originalScope ->getType ($ exprToSpecify );
17651773 if (!$ originalExprType ->equals ($ exprTypeWithoutNull )) {
17661774 $ originalNativeType = $ originalScope ->getNativeType ($ exprToSpecify );
17671775
17681776 return new EnsuredNonNullabilityResult ($ scope , [
1769- new EnsuredNonNullabilityResultExpression ($ exprToSpecify , $ originalExprType , $ originalNativeType ),
1777+ new EnsuredNonNullabilityResultExpression ($ exprToSpecify , $ originalExprType , $ originalNativeType, $ certainty ),
17701778 ]);
17711779 }
17721780 return new EnsuredNonNullabilityResult ($ scope , []);
@@ -1782,7 +1790,7 @@ private function ensureShallowNonNullability(MutatingScope $scope, Scope $origin
17821790 return new EnsuredNonNullabilityResult (
17831791 $ scope ,
17841792 [
1785- new EnsuredNonNullabilityResultExpression ($ exprToSpecify , $ exprType , $ nativeType ),
1793+ new EnsuredNonNullabilityResultExpression ($ exprToSpecify , $ exprType , $ nativeType, $ certainty ),
17861794 ],
17871795 );
17881796 }
@@ -1812,6 +1820,7 @@ private function revertNonNullability(MutatingScope $scope, array $specifiedExpr
18121820 $ specifiedExpressionResult ->getExpression (),
18131821 $ specifiedExpressionResult ->getOriginalType (),
18141822 $ specifiedExpressionResult ->getOriginalNativeType (),
1823+ $ specifiedExpressionResult ->getCertainty (),
18151824 );
18161825 }
18171826
@@ -2568,7 +2577,7 @@ static function (): void {
25682577 $ scope = $ this ->revertNonNullability ($ condResult ->getScope (), $ nonNullabilityResult ->getSpecifiedExpressions ());
25692578 $ scope = $ this ->lookForUnsetAllowedUndefinedExpressions ($ scope , $ expr ->left );
25702579
2571- $ rightScope = $ scope ->filterByFalseyValue (new Expr \ Isset_ ([ $ expr-> left ]) );
2580+ $ rightScope = $ scope ->filterByFalseyValue ($ expr );
25722581 $ rightResult = $ this ->processExprNode ($ expr ->right , $ rightScope , $ nodeCallback , $ context ->enterDeep ());
25732582 $ rightExprType = $ scope ->getType ($ expr ->right );
25742583 if ($ rightExprType instanceof NeverType && $ rightExprType ->isExplicit ()) {
@@ -2787,15 +2796,22 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
27872796 $ throwPoints = array_merge ($ throwPoints , $ elseResult ->getThrowPoints ());
27882797 $ ifFalseScope = $ elseResult ->getScope ();
27892798
2790- if ($ ifTrueType instanceof NeverType && $ ifTrueType ->isExplicit ()) {
2799+ $ condType = $ scope ->getType ($ expr ->cond );
2800+ if ($ condType ->isTrue ()->yes ()) {
2801+ $ finalScope = $ ifTrueScope ;
2802+ } elseif ($ condType ->isFalse ()->yes ()) {
27912803 $ finalScope = $ ifFalseScope ;
27922804 } else {
2793- $ ifFalseType = $ ifFalseScope ->getType ($ expr ->else );
2794-
2795- if ($ ifFalseType instanceof NeverType && $ ifFalseType ->isExplicit ()) {
2796- $ finalScope = $ ifTrueScope ;
2805+ if ($ ifTrueType instanceof NeverType && $ ifTrueType ->isExplicit ()) {
2806+ $ finalScope = $ ifFalseScope ;
27972807 } else {
2798- $ finalScope = $ ifTrueScope ->mergeWith ($ ifFalseScope );
2808+ $ ifFalseType = $ ifFalseScope ->getType ($ expr ->else );
2809+
2810+ if ($ ifFalseType instanceof NeverType && $ ifFalseType ->isExplicit ()) {
2811+ $ finalScope = $ ifTrueScope ;
2812+ } else {
2813+ $ finalScope = $ ifTrueScope ->mergeWith ($ ifFalseScope );
2814+ }
27992815 }
28002816 }
28012817
@@ -3751,10 +3767,35 @@ private function processAssignVar(
37513767 $ throwPoints = $ result ->getThrowPoints ();
37523768 $ assignedExpr = $ this ->unwrapAssign ($ assignedExpr );
37533769 $ type = $ scope ->getType ($ assignedExpr );
3754- $ truthySpecifiedTypes = $ this ->typeSpecifier ->specifyTypesInCondition ($ scope , $ assignedExpr , TypeSpecifierContext::createTruthy ());
3755- $ falseySpecifiedTypes = $ this ->typeSpecifier ->specifyTypesInCondition ($ scope , $ assignedExpr , TypeSpecifierContext::createFalsey ());
37563770
37573771 $ conditionalExpressions = [];
3772+ if ($ assignedExpr instanceof Ternary) {
3773+ $ if = $ assignedExpr ->if ;
3774+ if ($ if === null ) {
3775+ $ if = $ assignedExpr ->cond ;
3776+ }
3777+ $ condScope = $ this ->processExprNode ($ assignedExpr ->cond , $ scope , static function (): void {
3778+ }, ExpressionContext::createDeep ())->getScope ();
3779+ $ truthySpecifiedTypes = $ this ->typeSpecifier ->specifyTypesInCondition ($ condScope , $ assignedExpr ->cond , TypeSpecifierContext::createTruthy ());
3780+ $ falseySpecifiedTypes = $ this ->typeSpecifier ->specifyTypesInCondition ($ condScope , $ assignedExpr ->cond , TypeSpecifierContext::createFalsey ());
3781+ $ truthyScope = $ condScope ->filterBySpecifiedTypes ($ truthySpecifiedTypes );
3782+ $ falsyScope = $ condScope ->filterBySpecifiedTypes ($ falseySpecifiedTypes );
3783+ $ truthyType = $ truthyScope ->getType ($ if );
3784+ $ falseyType = $ falsyScope ->getType ($ assignedExpr ->else );
3785+
3786+ if (
3787+ $ truthyType ->isSuperTypeOf ($ falseyType )->no ()
3788+ && $ falseyType ->isSuperTypeOf ($ truthyType )->no ()
3789+ ) {
3790+ $ conditionalExpressions = $ this ->processSureTypesForConditionalExpressionsAfterAssign ($ condScope , $ var ->name , $ conditionalExpressions , $ truthySpecifiedTypes , $ truthyType );
3791+ $ conditionalExpressions = $ this ->processSureNotTypesForConditionalExpressionsAfterAssign ($ condScope , $ var ->name , $ conditionalExpressions , $ truthySpecifiedTypes , $ truthyType );
3792+ $ conditionalExpressions = $ this ->processSureTypesForConditionalExpressionsAfterAssign ($ condScope , $ var ->name , $ conditionalExpressions , $ falseySpecifiedTypes , $ falseyType );
3793+ $ conditionalExpressions = $ this ->processSureNotTypesForConditionalExpressionsAfterAssign ($ condScope , $ var ->name , $ conditionalExpressions , $ falseySpecifiedTypes , $ falseyType );
3794+ }
3795+ }
3796+
3797+ $ truthySpecifiedTypes = $ this ->typeSpecifier ->specifyTypesInCondition ($ scope , $ assignedExpr , TypeSpecifierContext::createTruthy ());
3798+ $ falseySpecifiedTypes = $ this ->typeSpecifier ->specifyTypesInCondition ($ scope , $ assignedExpr , TypeSpecifierContext::createFalsey ());
37583799
37593800 $ truthyType = TypeCombinator::removeFalsey ($ type );
37603801 $ falseyType = TypeCombinator::intersect ($ type , StaticTypeFactory::falsey ());
@@ -3764,7 +3805,6 @@ private function processAssignVar(
37643805 $ conditionalExpressions = $ this ->processSureTypesForConditionalExpressionsAfterAssign ($ scope , $ var ->name , $ conditionalExpressions , $ falseySpecifiedTypes , $ falseyType );
37653806 $ conditionalExpressions = $ this ->processSureNotTypesForConditionalExpressionsAfterAssign ($ scope , $ var ->name , $ conditionalExpressions , $ falseySpecifiedTypes , $ falseyType );
37663807
3767- // TODO conditional expressions for native type should be handled too
37683808 $ scope = $ result ->getScope ()->assignVariable ($ var ->name , $ type , $ scope ->getNativeType ($ assignedExpr ));
37693809 foreach ($ conditionalExpressions as $ exprString => $ holders ) {
37703810 $ scope = $ scope ->addConditionalExpressions ($ exprString , $ holders );
0 commit comments