@@ -272,7 +272,6 @@ public function specifyTypesInCondition(
272
272
) {
273
273
$ argType = $ scope ->getType ($ expr ->right ->getArgs ()[0 ]->value );
274
274
275
- $ sizeType = null ;
276
275
if ($ leftType instanceof ConstantIntegerType) {
277
276
if ($ orEqual ) {
278
277
$ sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo ($ leftType ->getValue ());
@@ -281,6 +280,8 @@ public function specifyTypesInCondition(
281
280
}
282
281
} elseif ($ leftType instanceof IntegerRangeType) {
283
282
$ sizeType = $ leftType ->shift ($ offset );
283
+ } else {
284
+ $ sizeType = $ leftType ;
284
285
}
285
286
286
287
$ specifiedTypes = $ this ->specifyTypesForCountFuncCall ($ expr ->right , $ argType , $ sizeType , $ context , $ scope , $ expr );
@@ -970,103 +971,91 @@ public function specifyTypesInCondition(
970
971
return (new SpecifiedTypes ([], []))->setRootExpr ($ expr );
971
972
}
972
973
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
974
982
{
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 ());
977
988
}
978
989
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 ;
1001
992
}
1002
993
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
+ }
1005
998
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 ();
1018
1005
}
1019
- return $ valueTypesBuilder ->getArray ();
1020
- }
1021
1006
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 ));
1044
1017
}
1045
- $ valueTypesBuilder ->setOffsetValueType ( $ offsetType , $ type -> getOffsetValueType ( $ offsetType ), ! $ hasOffset -> yes () );
1018
+ return $ valueTypesBuilder ->getArray ( );
1046
1019
}
1047
- } else {
1048
- return null ;
1049
- }
1050
1020
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
+ }
1056
1048
1057
- return null ;
1058
- }
1049
+ return $ valueTypesBuilder -> getArray () ;
1050
+ }
1059
1051
1060
- private function isFuncCallWithNormalCount (FuncCall $ countFuncCall , Scope $ scope ): TrinaryLogic
1061
- {
1062
- $ argType = $ scope ->getType ($ countFuncCall ->getArgs ()[0 ]->value );
1052
+ return $ type ;
1053
+ }
1063
1054
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
+ });
1068
1057
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 );
1070
1059
}
1071
1060
1072
1061
private function specifyTypesForConstantBinaryExpression (
@@ -2103,30 +2092,15 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
2103
2092
return $ specifiedTypes ;
2104
2093
}
2105
2094
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
+ );
2129
2101
}
2102
+
2103
+ return $ funcTypes ;
2130
2104
}
2131
2105
}
2132
2106
0 commit comments