@@ -272,7 +272,6 @@ public function specifyTypesInCondition(
272272 ) {
273273 $ argType = $ scope ->getType ($ expr ->right ->getArgs ()[0 ]->value );
274274
275- $ sizeType = null ;
276275 if ($ leftType instanceof ConstantIntegerType) {
277276 if ($ orEqual ) {
278277 $ sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo ($ leftType ->getValue ());
@@ -281,6 +280,8 @@ public function specifyTypesInCondition(
281280 }
282281 } elseif ($ leftType instanceof IntegerRangeType) {
283282 $ sizeType = $ leftType ->shift ($ offset );
283+ } else {
284+ $ sizeType = $ leftType ;
284285 }
285286
286287 $ specifiedTypes = $ this ->specifyTypesForCountFuncCall ($ expr ->right , $ argType , $ sizeType , $ context , $ scope , $ expr );
@@ -970,103 +971,91 @@ public function specifyTypesInCondition(
970971 return (new SpecifiedTypes ([], []))->setRootExpr ($ expr );
971972 }
972973
973- private function specifyTypesForCountFuncCall (FuncCall $ countFuncCall , Type $ type , ?Type $ sizeType , TypeSpecifierContext $ context , Scope $ scope , ?Expr $ rootExpr ): ?SpecifiedTypes
974+ private function specifyTypesForCountFuncCall (
975+ FuncCall $ countFuncCall ,
976+ Type $ type ,
977+ Type $ sizeType ,
978+ TypeSpecifierContext $ context ,
979+ Scope $ scope ,
980+ Expr $ rootExpr ,
981+ ): ?SpecifiedTypes
974982 {
975- if ($ sizeType === null ) {
976- return null ;
983+ if (count ($ countFuncCall ->getArgs ()) === 1 ) {
984+ $ isNormalCount = TrinaryLogic::createYes ();
985+ } else {
986+ $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
987+ $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->result ->or ($ type ->getIterableValueType ()->isArray ()->negate ());
977988 }
978989
979- if (
980- $ this ->isFuncCallWithNormalCount ($ countFuncCall , $ scope )->yes ()
981- && $ type ->isConstantArray ()->yes ()
982- ) {
983- $ resultType = TypeTraverser::map ($ type , function (Type $ type , callable $ traverse ) use ($ sizeType , $ context ) {
984- if ($ type instanceof UnionType) {
985- return $ traverse ($ type );
986- }
987-
988- $ arraySize = $ type ->getArraySize ();
989- $ isSize = $ sizeType ->isSuperTypeOf ($ arraySize );
990- if ($ context ->truthy () && $ isSize ->no ()) {
991- return new NeverType ();
992- }
993- if ($ context ->falsey () && !$ isSize ->yes ()) {
994- return new NeverType ();
995- }
996-
997- return $ this ->turnListIntoConstantArray ($ type , $ sizeType ) ?? $ type ;
998- });
999-
1000- return $ this ->create ($ countFuncCall ->getArgs ()[0 ]->value , $ resultType , $ context , $ scope )->setRootExpr ($ rootExpr );
990+ if (!$ isNormalCount ->yes () || (!$ type ->isConstantArray ()->yes () && !$ type ->isList ()->yes ())) {
991+ return null ;
1001992 }
1002993
1003- return null ;
1004- }
994+ $ resultType = TypeTraverser::map ($ type , static function (Type $ type , callable $ traverse ) use ($ sizeType , $ context ) {
995+ if ($ type instanceof UnionType) {
996+ return $ traverse ($ type );
997+ }
1005998
1006- private function turnListIntoConstantArray (Type $ type , Type $ sizeType ): ?Type
1007- {
1008- if (
1009- $ type ->isList ()->yes ()
1010- && $ sizeType instanceof ConstantIntegerType
1011- && $ sizeType ->getValue () < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1012- ) {
1013- // turn optional offsets non-optional
1014- $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1015- for ($ i = 0 ; $ i < $ sizeType ->getValue (); $ i ++) {
1016- $ offsetType = new ConstantIntegerType ($ i );
1017- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
999+ $ isSizeSuperTypeOfArraySize = $ sizeType ->isSuperTypeOf ($ type ->getArraySize ());
1000+ if ($ context ->truthy () && $ isSizeSuperTypeOfArraySize ->no ()) {
1001+ return new NeverType ();
1002+ }
1003+ if ($ context ->falsey () && !$ isSizeSuperTypeOfArraySize ->yes ()) {
1004+ return new NeverType ();
10181005 }
1019- return $ valueTypesBuilder ->getArray ();
1020- }
10211006
1022- if (
1023- $ type ->isList ()->yes ()
1024- && $ sizeType instanceof IntegerRangeType
1025- && $ sizeType ->getMin () !== null
1026- ) {
1027- // turn optional offsets non-optional
1028- $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1029- for ($ i = 0 ; $ i < $ sizeType ->getMin (); $ i ++) {
1030- $ offsetType = new ConstantIntegerType ($ i );
1031- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
1032- }
1033- if ($ sizeType ->getMax () !== null ) {
1034- for ($ i = $ sizeType ->getMin (); $ i < $ sizeType ->getMax (); $ i ++) {
1035- $ offsetType = new ConstantIntegerType ($ i );
1036- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), true );
1037- }
1038- } elseif ($ type ->isConstantArray ()->yes ()) {
1039- for ($ i = $ sizeType ->getMin ();; $ i ++) {
1040- $ offsetType = new ConstantIntegerType ($ i );
1041- $ hasOffset = $ type ->hasOffsetValueType ($ offsetType );
1042- if ($ hasOffset ->no ()) {
1043- break ;
1007+ if ($ type ->isList ()->yes ()) {
1008+ if (
1009+ $ sizeType instanceof ConstantIntegerType
1010+ && $ sizeType ->getValue () < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1011+ ) {
1012+ // turn optional offsets non-optional
1013+ $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1014+ for ($ i = 0 ; $ i < $ sizeType ->getValue (); $ i ++) {
1015+ $ offsetType = new ConstantIntegerType ($ i );
1016+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
10441017 }
1045- $ valueTypesBuilder ->setOffsetValueType ( $ offsetType , $ type -> getOffsetValueType ( $ offsetType ), ! $ hasOffset -> yes () );
1018+ return $ valueTypesBuilder ->getArray ( );
10461019 }
1047- } else {
1048- return null ;
1049- }
10501020
1051- $ arrayType = $ valueTypesBuilder ->getArray ();
1052- if ($ arrayType ->isIterableAtLeastOnce ()->yes ()) {
1053- return $ arrayType ;
1054- }
1055- }
1021+ if (
1022+ $ sizeType instanceof IntegerRangeType
1023+ && $ sizeType ->getMin () !== null
1024+ ) {
1025+ // turn optional offsets non-optional
1026+ $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1027+ for ($ i = 0 ; $ i < $ sizeType ->getMin (); $ i ++) {
1028+ $ offsetType = new ConstantIntegerType ($ i );
1029+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
1030+ }
1031+ if ($ sizeType ->getMax () !== null ) {
1032+ for ($ i = $ sizeType ->getMin (); $ i < $ sizeType ->getMax (); $ i ++) {
1033+ $ offsetType = new ConstantIntegerType ($ i );
1034+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), true );
1035+ }
1036+ } elseif ($ type ->isConstantArray ()->yes ()) {
1037+ for ($ i = $ sizeType ->getMin ();; $ i ++) {
1038+ $ offsetType = new ConstantIntegerType ($ i );
1039+ $ hasOffset = $ type ->hasOffsetValueType ($ offsetType );
1040+ if ($ hasOffset ->no ()) {
1041+ break ;
1042+ }
1043+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), !$ hasOffset ->yes ());
1044+ }
1045+ } else {
1046+ return TypeCombinator::intersect ($ type , new NonEmptyArrayType ());
1047+ }
10561048
1057- return null ;
1058- }
1049+ return $ valueTypesBuilder -> getArray () ;
1050+ }
10591051
1060- private function isFuncCallWithNormalCount (FuncCall $ countFuncCall , Scope $ scope ): TrinaryLogic
1061- {
1062- $ argType = $ scope ->getType ($ countFuncCall ->getArgs ()[0 ]->value );
1052+ return $ type ;
1053+ }
10631054
1064- if (count ($ countFuncCall ->getArgs ()) === 1 ) {
1065- return TrinaryLogic::createYes ();
1066- }
1067- $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
1055+ return TypeCombinator::intersect ($ type , new NonEmptyArrayType ());
1056+ });
10681057
1069- return ( new ConstantIntegerType ( COUNT_NORMAL ))-> isSuperTypeOf ( $ mode )-> result -> or ( $ argType -> getIterableValueType ()-> isArray ( )->negate () );
1058+ return $ this -> create ( $ countFuncCall -> getArgs ()[ 0 ]-> value , $ resultType , $ context , $ scope )->setRootExpr ( $ rootExpr );
10701059 }
10711060
10721061 private function specifyTypesForConstantBinaryExpression (
@@ -2103,30 +2092,15 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
21032092 return $ specifiedTypes ;
21042093 }
21052094
2106- if ($ context ->truthy ()) {
2107- if ($ argType ->isArray ()->yes ()) {
2108- if (
2109- $ argType ->isConstantArray ()->yes ()
2110- && $ rightType ->isSuperTypeOf ($ argType ->getArraySize ())->no ()
2111- ) {
2112- return $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NeverType (), $ context , $ scope )->setRootExpr ($ expr );
2113- }
2114-
2115- $ funcTypes = $ this ->create ($ unwrappedLeftExpr , $ rightType , $ context , $ scope )->setRootExpr ($ expr );
2116- $ isNormalCount = $ this ->isFuncCallWithNormalCount ($ unwrappedLeftExpr , $ scope );
2117- $ constArray = $ isNormalCount ->yes () ? $ this ->turnListIntoConstantArray ($ argType , $ rightType ) : null ;
2118- if ($ constArray !== null ) {
2119- return $ funcTypes ->unionWith (
2120- $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , $ constArray , $ context , $ scope )->setRootExpr ($ expr ),
2121- );
2122- } elseif (IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ rightType )->yes ()) {
2123- return $ funcTypes ->unionWith (
2124- $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NonEmptyArrayType (), $ context , $ scope )->setRootExpr ($ expr ),
2125- );
2126- }
2127-
2128- return $ funcTypes ;
2095+ if ($ context ->truthy () && $ argType ->isArray ()->yes ()) {
2096+ $ funcTypes = $ this ->create ($ unwrappedLeftExpr , $ rightType , $ context , $ scope )->setRootExpr ($ expr );
2097+ if (IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ rightType )->yes ()) {
2098+ return $ funcTypes ->unionWith (
2099+ $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NonEmptyArrayType (), $ context , $ scope )->setRootExpr ($ expr ),
2100+ );
21292101 }
2102+
2103+ return $ funcTypes ;
21302104 }
21312105 }
21322106
0 commit comments