@@ -269,7 +269,6 @@ public function specifyTypesInCondition(
269
269
) {
270
270
$ argType = $ scope ->getType ($ expr ->right ->getArgs ()[0 ]->value );
271
271
272
- $ sizeType = null ;
273
272
if ($ leftType instanceof ConstantIntegerType) {
274
273
if ($ orEqual ) {
275
274
$ sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo ($ leftType ->getValue ());
@@ -278,6 +277,8 @@ public function specifyTypesInCondition(
278
277
}
279
278
} elseif ($ leftType instanceof IntegerRangeType) {
280
279
$ sizeType = $ leftType ->shift ($ offset );
280
+ } else {
281
+ $ sizeType = $ leftType ;
281
282
}
282
283
283
284
$ specifiedTypes = $ this ->specifyTypesForCountFuncCall ($ expr ->right , $ argType , $ sizeType , $ context , $ scope , $ expr );
@@ -967,103 +968,91 @@ public function specifyTypesInCondition(
967
968
return (new SpecifiedTypes ([], []))->setRootExpr ($ expr );
968
969
}
969
970
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
971
979
{
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 ());
974
985
}
975
986
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 ;
998
989
}
999
990
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
+ }
1002
995
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 ();
1015
1002
}
1016
- return $ valueTypesBuilder ->getArray ();
1017
- }
1018
1003
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 ));
1041
1014
}
1042
- $ valueTypesBuilder ->setOffsetValueType ( $ offsetType , $ type -> getOffsetValueType ( $ offsetType ), ! $ hasOffset -> yes () );
1015
+ return $ valueTypesBuilder ->getArray ( );
1043
1016
}
1044
- } else {
1045
- return null ;
1046
- }
1047
1017
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
+ }
1053
1045
1054
- return null ;
1055
- }
1046
+ return $ valueTypesBuilder -> getArray () ;
1047
+ }
1056
1048
1057
- private function isFuncCallWithNormalCount (FuncCall $ countFuncCall , Scope $ scope ): TrinaryLogic
1058
- {
1059
- $ argType = $ scope ->getType ($ countFuncCall ->getArgs ()[0 ]->value );
1049
+ return $ type ;
1050
+ }
1060
1051
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
+ });
1065
1054
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 );
1067
1056
}
1068
1057
1069
1058
private function specifyTypesForConstantBinaryExpression (
@@ -2100,30 +2089,15 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
2100
2089
return $ specifiedTypes ;
2101
2090
}
2102
2091
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
+ );
2126
2098
}
2099
+
2100
+ return $ funcTypes ;
2127
2101
}
2128
2102
}
2129
2103
0 commit comments