@@ -269,7 +269,6 @@ public function specifyTypesInCondition(
269269 ) {
270270 $ argType = $ scope ->getType ($ expr ->right ->getArgs ()[0 ]->value );
271271
272- $ sizeType = null ;
273272 if ($ leftType instanceof ConstantIntegerType) {
274273 if ($ orEqual ) {
275274 $ sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo ($ leftType ->getValue ());
@@ -278,6 +277,8 @@ public function specifyTypesInCondition(
278277 }
279278 } elseif ($ leftType instanceof IntegerRangeType) {
280279 $ sizeType = $ leftType ->shift ($ offset );
280+ } else {
281+ $ sizeType = $ leftType ;
281282 }
282283
283284 $ specifiedTypes = $ this ->specifyTypesForCountFuncCall ($ expr ->right , $ argType , $ sizeType , $ context , $ scope , $ expr );
@@ -967,103 +968,91 @@ public function specifyTypesInCondition(
967968 return (new SpecifiedTypes ([], []))->setRootExpr ($ expr );
968969 }
969970
970- private function specifyTypesForCountFuncCall (FuncCall $ countFuncCall , Type $ type , ?Type $ sizeType , TypeSpecifierContext $ context , Scope $ scope , ?Expr $ rootExpr ): ?SpecifiedTypes
971+ private function specifyTypesForCountFuncCall (
972+ FuncCall $ countFuncCall ,
973+ Type $ type ,
974+ Type $ sizeType ,
975+ TypeSpecifierContext $ context ,
976+ Scope $ scope ,
977+ Expr $ rootExpr ,
978+ ): ?SpecifiedTypes
971979 {
972- if ($ sizeType === null ) {
973- return null ;
980+ if (count ($ countFuncCall ->getArgs ()) === 1 ) {
981+ $ isNormalCount = TrinaryLogic::createYes ();
982+ } else {
983+ $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
984+ $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->result ->or ($ type ->getIterableValueType ()->isArray ()->negate ());
974985 }
975986
976- if (
977- $ this ->isFuncCallWithNormalCount ($ countFuncCall , $ scope )->yes ()
978- && $ type ->isConstantArray ()->yes ()
979- ) {
980- $ resultType = TypeTraverser::map ($ type , function (Type $ type , callable $ traverse ) use ($ sizeType , $ context ) {
981- if ($ type instanceof UnionType) {
982- return $ traverse ($ type );
983- }
984-
985- $ arraySize = $ type ->getArraySize ();
986- $ isSize = $ sizeType ->isSuperTypeOf ($ arraySize );
987- if ($ context ->truthy () && $ isSize ->no ()) {
988- return new NeverType ();
989- }
990- if ($ context ->falsey () && !$ isSize ->yes ()) {
991- return new NeverType ();
992- }
993-
994- return $ this ->turnListIntoConstantArray ($ type , $ sizeType ) ?? $ type ;
995- });
996-
997- return $ this ->create ($ countFuncCall ->getArgs ()[0 ]->value , $ resultType , $ context , $ scope )->setRootExpr ($ rootExpr );
987+ if (!$ isNormalCount ->yes () || (!$ type ->isConstantArray ()->yes () && !$ type ->isList ()->yes ())) {
988+ return null ;
998989 }
999990
1000- return null ;
1001- }
991+ $ resultType = TypeTraverser::map ($ type , static function (Type $ type , callable $ traverse ) use ($ sizeType , $ context ) {
992+ if ($ type instanceof UnionType) {
993+ return $ traverse ($ type );
994+ }
1002995
1003- private function turnListIntoConstantArray (Type $ type , Type $ sizeType ): ?Type
1004- {
1005- if (
1006- $ type ->isList ()->yes ()
1007- && $ sizeType instanceof ConstantIntegerType
1008- && $ sizeType ->getValue () < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1009- ) {
1010- // turn optional offsets non-optional
1011- $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1012- for ($ i = 0 ; $ i < $ sizeType ->getValue (); $ i ++) {
1013- $ offsetType = new ConstantIntegerType ($ i );
1014- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
996+ $ isSizeSuperTypeOfArraySize = $ sizeType ->isSuperTypeOf ($ type ->getArraySize ());
997+ if ($ context ->truthy () && $ isSizeSuperTypeOfArraySize ->no ()) {
998+ return new NeverType ();
999+ }
1000+ if ($ context ->falsey () && !$ isSizeSuperTypeOfArraySize ->yes ()) {
1001+ return new NeverType ();
10151002 }
1016- return $ valueTypesBuilder ->getArray ();
1017- }
10181003
1019- if (
1020- $ type ->isList ()->yes ()
1021- && $ sizeType instanceof IntegerRangeType
1022- && $ sizeType ->getMin () !== null
1023- ) {
1024- // turn optional offsets non-optional
1025- $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1026- for ($ i = 0 ; $ i < $ sizeType ->getMin (); $ i ++) {
1027- $ offsetType = new ConstantIntegerType ($ i );
1028- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
1029- }
1030- if ($ sizeType ->getMax () !== null ) {
1031- for ($ i = $ sizeType ->getMin (); $ i < $ sizeType ->getMax (); $ i ++) {
1032- $ offsetType = new ConstantIntegerType ($ i );
1033- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), true );
1034- }
1035- } elseif ($ type ->isConstantArray ()->yes ()) {
1036- for ($ i = $ sizeType ->getMin ();; $ i ++) {
1037- $ offsetType = new ConstantIntegerType ($ i );
1038- $ hasOffset = $ type ->hasOffsetValueType ($ offsetType );
1039- if ($ hasOffset ->no ()) {
1040- break ;
1004+ if ($ type ->isList ()->yes ()) {
1005+ if (
1006+ $ sizeType instanceof ConstantIntegerType
1007+ && $ sizeType ->getValue () < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1008+ ) {
1009+ // turn optional offsets non-optional
1010+ $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1011+ for ($ i = 0 ; $ i < $ sizeType ->getValue (); $ i ++) {
1012+ $ offsetType = new ConstantIntegerType ($ i );
1013+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
10411014 }
1042- $ valueTypesBuilder ->setOffsetValueType ( $ offsetType , $ type -> getOffsetValueType ( $ offsetType ), ! $ hasOffset -> yes () );
1015+ return $ valueTypesBuilder ->getArray ( );
10431016 }
1044- } else {
1045- return null ;
1046- }
10471017
1048- $ arrayType = $ valueTypesBuilder ->getArray ();
1049- if ($ arrayType ->isIterableAtLeastOnce ()->yes ()) {
1050- return $ arrayType ;
1051- }
1052- }
1018+ if (
1019+ $ sizeType instanceof IntegerRangeType
1020+ && $ sizeType ->getMin () !== null
1021+ ) {
1022+ // turn optional offsets non-optional
1023+ $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1024+ for ($ i = 0 ; $ i < $ sizeType ->getMin (); $ i ++) {
1025+ $ offsetType = new ConstantIntegerType ($ i );
1026+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
1027+ }
1028+ if ($ sizeType ->getMax () !== null ) {
1029+ for ($ i = $ sizeType ->getMin (); $ i < $ sizeType ->getMax (); $ i ++) {
1030+ $ offsetType = new ConstantIntegerType ($ i );
1031+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), true );
1032+ }
1033+ } elseif ($ type ->isConstantArray ()->yes ()) {
1034+ for ($ i = $ sizeType ->getMin ();; $ i ++) {
1035+ $ offsetType = new ConstantIntegerType ($ i );
1036+ $ hasOffset = $ type ->hasOffsetValueType ($ offsetType );
1037+ if ($ hasOffset ->no ()) {
1038+ break ;
1039+ }
1040+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), !$ hasOffset ->yes ());
1041+ }
1042+ } else {
1043+ return TypeCombinator::intersect ($ type , new NonEmptyArrayType ());
1044+ }
10531045
1054- return null ;
1055- }
1046+ return $ valueTypesBuilder -> getArray () ;
1047+ }
10561048
1057- private function isFuncCallWithNormalCount (FuncCall $ countFuncCall , Scope $ scope ): TrinaryLogic
1058- {
1059- $ argType = $ scope ->getType ($ countFuncCall ->getArgs ()[0 ]->value );
1049+ return $ type ;
1050+ }
10601051
1061- if (count ($ countFuncCall ->getArgs ()) === 1 ) {
1062- return TrinaryLogic::createYes ();
1063- }
1064- $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
1052+ return TypeCombinator::intersect ($ type , new NonEmptyArrayType ());
1053+ });
10651054
1066- return ( new ConstantIntegerType ( COUNT_NORMAL ))-> isSuperTypeOf ( $ mode )-> result -> or ( $ argType -> getIterableValueType ()-> isArray ( )->negate () );
1055+ return $ this -> create ( $ countFuncCall -> getArgs ()[ 0 ]-> value , $ resultType , $ context , $ scope )->setRootExpr ( $ rootExpr );
10671056 }
10681057
10691058 private function specifyTypesForConstantBinaryExpression (
@@ -2100,30 +2089,15 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
21002089 return $ specifiedTypes ;
21012090 }
21022091
2103- if ($ context ->truthy ()) {
2104- if ($ argType ->isArray ()->yes ()) {
2105- if (
2106- $ argType ->isConstantArray ()->yes ()
2107- && $ rightType ->isSuperTypeOf ($ argType ->getArraySize ())->no ()
2108- ) {
2109- return $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NeverType (), $ context , $ scope )->setRootExpr ($ expr );
2110- }
2111-
2112- $ funcTypes = $ this ->create ($ unwrappedLeftExpr , $ rightType , $ context , $ scope )->setRootExpr ($ expr );
2113- $ isNormalCount = $ this ->isFuncCallWithNormalCount ($ unwrappedLeftExpr , $ scope );
2114- $ constArray = $ isNormalCount ->yes () ? $ this ->turnListIntoConstantArray ($ argType , $ rightType ) : null ;
2115- if ($ constArray !== null ) {
2116- return $ funcTypes ->unionWith (
2117- $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , $ constArray , $ context , $ scope )->setRootExpr ($ expr ),
2118- );
2119- } elseif (IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ rightType )->yes ()) {
2120- return $ funcTypes ->unionWith (
2121- $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NonEmptyArrayType (), $ context , $ scope )->setRootExpr ($ expr ),
2122- );
2123- }
2124-
2125- return $ funcTypes ;
2092+ if ($ context ->truthy () && $ argType ->isArray ()->yes ()) {
2093+ $ funcTypes = $ this ->create ($ unwrappedLeftExpr , $ rightType , $ context , $ scope )->setRootExpr ($ expr );
2094+ if (IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ rightType )->yes ()) {
2095+ return $ funcTypes ->unionWith (
2096+ $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NonEmptyArrayType (), $ context , $ scope )->setRootExpr ($ expr ),
2097+ );
21262098 }
2099+
2100+ return $ funcTypes ;
21272101 }
21282102 }
21292103
0 commit comments