@@ -493,63 +493,123 @@ public function specifyTypesInCondition(
493493 }
494494
495495 $ leftExpr = $ expr ->left ;
496+ $ leftPartialCast = null ;
496497 if ($ leftExpr instanceof Expr \Cast) {
497498 $ castedType = $ scope ->getType ($ leftExpr );
498499 $ innerType = $ scope ->getType ($ leftExpr ->expr );
499500 if ($ castedType ->equals ($ innerType )) {
500501 $ leftExpr = $ leftExpr ->expr ;
502+ } elseif ($ innerType instanceof UnionType) {
503+ $ nonRedundant = $ this ->filterNonRedundantCastTypes ($ leftExpr , $ innerType );
504+ if ($ nonRedundant !== null ) {
505+ $ leftPartialCast = [$ leftExpr ->expr , $ castedType , $ nonRedundant ];
506+ }
501507 }
502508 }
503509 $ rightExpr = $ expr ->right ;
510+ $ rightPartialCast = null ;
504511 if ($ rightExpr instanceof Expr \Cast) {
505512 $ castedType = $ scope ->getType ($ rightExpr );
506513 $ innerType = $ scope ->getType ($ rightExpr ->expr );
507514 if ($ castedType ->equals ($ innerType )) {
508515 $ rightExpr = $ rightExpr ->expr ;
516+ } elseif ($ innerType instanceof UnionType) {
517+ $ nonRedundant = $ this ->filterNonRedundantCastTypes ($ rightExpr , $ innerType );
518+ if ($ nonRedundant !== null ) {
519+ $ rightPartialCast = [$ rightExpr ->expr , $ castedType , $ nonRedundant ];
520+ }
509521 }
510522 }
511523
512524 if ($ context ->true ()) {
513525 if (!$ leftExpr instanceof Node \Scalar) {
526+ $ narrowedLeftType = $ orEqual ? $ rightType ->getSmallerOrEqualType ($ this ->phpVersion ) : $ rightType ->getSmallerType ($ this ->phpVersion );
514527 $ result = $ result ->unionWith (
515528 $ this ->create (
516529 $ leftExpr ,
517- $ orEqual ? $ rightType -> getSmallerOrEqualType ( $ this -> phpVersion ) : $ rightType -> getSmallerType ( $ this -> phpVersion ) ,
530+ $ narrowedLeftType ,
518531 TypeSpecifierContext::createTruthy (),
519532 $ scope ,
520533 )->setRootExpr ($ expr ),
521534 );
535+ if ($ leftPartialCast !== null ) {
536+ [$ innerExpr , $ castType , $ nonRedundantType ] = $ leftPartialCast ;
537+ $ result = $ result ->unionWith (
538+ $ this ->create (
539+ $ innerExpr ,
540+ TypeCombinator::union (TypeCombinator::intersect ($ narrowedLeftType , $ castType ), $ nonRedundantType ),
541+ TypeSpecifierContext::createTruthy (),
542+ $ scope ,
543+ )->setRootExpr ($ expr ),
544+ );
545+ }
522546 }
523547 if (!$ rightExpr instanceof Node \Scalar) {
548+ $ narrowedRightType = $ orEqual ? $ leftType ->getGreaterOrEqualType ($ this ->phpVersion ) : $ leftType ->getGreaterType ($ this ->phpVersion );
524549 $ result = $ result ->unionWith (
525550 $ this ->create (
526551 $ rightExpr ,
527- $ orEqual ? $ leftType -> getGreaterOrEqualType ( $ this -> phpVersion ) : $ leftType -> getGreaterType ( $ this -> phpVersion ) ,
552+ $ narrowedRightType ,
528553 TypeSpecifierContext::createTruthy (),
529554 $ scope ,
530555 )->setRootExpr ($ expr ),
531556 );
557+ if ($ rightPartialCast !== null ) {
558+ [$ innerExpr , $ castType , $ nonRedundantType ] = $ rightPartialCast ;
559+ $ result = $ result ->unionWith (
560+ $ this ->create (
561+ $ innerExpr ,
562+ TypeCombinator::union (TypeCombinator::intersect ($ narrowedRightType , $ castType ), $ nonRedundantType ),
563+ TypeSpecifierContext::createTruthy (),
564+ $ scope ,
565+ )->setRootExpr ($ expr ),
566+ );
567+ }
532568 }
533569 } elseif ($ context ->false ()) {
534570 if (!$ leftExpr instanceof Node \Scalar) {
571+ $ narrowedLeftType = $ orEqual ? $ rightType ->getGreaterType ($ this ->phpVersion ) : $ rightType ->getGreaterOrEqualType ($ this ->phpVersion );
535572 $ result = $ result ->unionWith (
536573 $ this ->create (
537574 $ leftExpr ,
538- $ orEqual ? $ rightType -> getGreaterType ( $ this -> phpVersion ) : $ rightType -> getGreaterOrEqualType ( $ this -> phpVersion ) ,
575+ $ narrowedLeftType ,
539576 TypeSpecifierContext::createTruthy (),
540577 $ scope ,
541578 )->setRootExpr ($ expr ),
542579 );
580+ if ($ leftPartialCast !== null ) {
581+ [$ innerExpr , $ castType , $ nonRedundantType ] = $ leftPartialCast ;
582+ $ result = $ result ->unionWith (
583+ $ this ->create (
584+ $ innerExpr ,
585+ TypeCombinator::union (TypeCombinator::intersect ($ narrowedLeftType , $ castType ), $ nonRedundantType ),
586+ TypeSpecifierContext::createTruthy (),
587+ $ scope ,
588+ )->setRootExpr ($ expr ),
589+ );
590+ }
543591 }
544592 if (!$ rightExpr instanceof Node \Scalar) {
593+ $ narrowedRightType = $ orEqual ? $ leftType ->getSmallerType ($ this ->phpVersion ) : $ leftType ->getSmallerOrEqualType ($ this ->phpVersion );
545594 $ result = $ result ->unionWith (
546595 $ this ->create (
547596 $ rightExpr ,
548- $ orEqual ? $ leftType -> getSmallerType ( $ this -> phpVersion ) : $ leftType -> getSmallerOrEqualType ( $ this -> phpVersion ) ,
597+ $ narrowedRightType ,
549598 TypeSpecifierContext::createTruthy (),
550599 $ scope ,
551600 )->setRootExpr ($ expr ),
552601 );
602+ if ($ rightPartialCast !== null ) {
603+ [$ innerExpr , $ castType , $ nonRedundantType ] = $ rightPartialCast ;
604+ $ result = $ result ->unionWith (
605+ $ this ->create (
606+ $ innerExpr ,
607+ TypeCombinator::union (TypeCombinator::intersect ($ narrowedRightType , $ castType ), $ nonRedundantType ),
608+ TypeSpecifierContext::createTruthy (),
609+ $ scope ,
610+ )->setRootExpr ($ expr ),
611+ );
612+ }
553613 }
554614 }
555615
@@ -3249,4 +3309,36 @@ private function resolveNormalizedIdentical(Expr\BinaryOp\Identical $expr, Scope
32493309 return (new SpecifiedTypes ([], []))->setRootExpr ($ expr );
32503310 }
32513311
3312+ private function filterNonRedundantCastTypes (Expr \Cast $ cast , UnionType $ innerType ): ?Type
3313+ {
3314+ $ nonRedundantTypes = [];
3315+ $ hasRedundant = false ;
3316+ foreach ($ innerType ->getTypes () as $ memberType ) {
3317+ $ convertedType = null ;
3318+ if ($ cast instanceof Expr \Cast \Int_) {
3319+ $ convertedType = $ memberType ->toInteger ();
3320+ } elseif ($ cast instanceof Expr \Cast \Double) {
3321+ $ convertedType = $ memberType ->toFloat ();
3322+ } elseif ($ cast instanceof Expr \Cast \String_) {
3323+ $ convertedType = $ memberType ->toString ();
3324+ } elseif ($ cast instanceof Expr \Cast \Bool_) {
3325+ $ convertedType = $ memberType ->toBoolean ();
3326+ } elseif ($ cast instanceof Expr \Cast \Array_) {
3327+ $ convertedType = $ memberType ->toArray ();
3328+ }
3329+
3330+ if ($ convertedType !== null && $ convertedType ->equals ($ memberType )) {
3331+ $ hasRedundant = true ;
3332+ } else {
3333+ $ nonRedundantTypes [] = $ memberType ;
3334+ }
3335+ }
3336+
3337+ if ($ hasRedundant && $ nonRedundantTypes !== []) {
3338+ return TypeCombinator::union (...$ nonRedundantTypes );
3339+ }
3340+
3341+ return null ;
3342+ }
3343+
32523344}
0 commit comments