@@ -244,12 +244,25 @@ public function specifyTypesInCondition(
244244 && in_array (strtolower ((string ) $ expr ->right ->name ), ['count ' , 'sizeof ' ], true )
245245 && $ leftType ->isInteger ()->yes ()
246246 ) {
247+ $ argType = $ scope ->getType ($ expr ->right ->getArgs ()[0 ]->value );
248+
249+ if ($ argType instanceof UnionType && $ leftType instanceof ConstantIntegerType) {
250+ if ($ orEqual ) {
251+ $ sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo ($ leftType ->getValue ());
252+ } else {
253+ $ sizeType = IntegerRangeType::createAllGreaterThan ($ leftType ->getValue ());
254+ }
255+
256+ $ narrowed = $ this ->narrowUnionByArraySize ($ expr ->right , $ argType , $ sizeType , $ context , $ scope , $ rootExpr );
257+ if ($ narrowed !== null ) {
258+ return $ narrowed ;
259+ }
260+ }
261+
247262 if (
248263 $ context ->true () && (IntegerRangeType::createAllGreaterThanOrEqualTo (1 - $ offset )->isSuperTypeOf ($ leftType )->yes ())
249264 || ($ context ->false () && (new ConstantIntegerType (1 - $ offset ))->isSuperTypeOf ($ leftType )->yes ())
250265 ) {
251- $ argType = $ scope ->getType ($ expr ->right ->getArgs ()[0 ]->value );
252-
253266 if ($ context ->truthy () && $ argType ->isArray ()->maybe ()) {
254267 $ countables = [];
255268 if ($ argType instanceof UnionType) {
@@ -936,6 +949,43 @@ public function specifyTypesInCondition(
936949 return new SpecifiedTypes ([], [], false , [], $ rootExpr );
937950 }
938951
952+ private function narrowUnionByArraySize (FuncCall $ countFuncCall , UnionType $ argType , Type $ sizeType , TypeSpecifierContext $ context , Scope $ scope , ?Expr $ rootExpr ): ?SpecifiedTypes
953+ {
954+ if (count ($ countFuncCall ->getArgs ()) === 1 ) {
955+ $ isNormalCount = TrinaryLogic::createYes ();
956+ } else {
957+ $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
958+ $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->or ($ argType ->getIterableValueType ()->isArray ()->negate ());
959+ }
960+
961+ if (
962+ $ isNormalCount ->yes ()
963+ && $ argType ->isConstantArray ()->yes ()
964+ ) {
965+ $ result = [];
966+ foreach ($ argType ->getTypes () as $ innerType ) {
967+ $ arraySize = $ innerType ->getArraySize ();
968+ $ isSize = $ sizeType ->isSuperTypeOf ($ arraySize );
969+ if ($ context ->truthy ()) {
970+ if ($ isSize ->no ()) {
971+ continue ;
972+ }
973+ }
974+ if ($ context ->falsey ()) {
975+ if (!$ isSize ->yes ()) {
976+ continue ;
977+ }
978+ }
979+
980+ $ result [] = $ innerType ;
981+ }
982+
983+ return $ this ->create ($ countFuncCall ->getArgs ()[0 ]->value , TypeCombinator::union (...$ result ), $ context , false , $ scope , $ rootExpr );
984+ }
985+
986+ return null ;
987+ }
988+
939989 private function specifyTypesForConstantBinaryExpression (
940990 Expr $ exprNode ,
941991 ConstantScalarType $ constantType ,
@@ -986,36 +1036,11 @@ private function specifyTypesForConstantBinaryExpression(
9861036 ) {
9871037 $ argType = $ scope ->getType ($ exprNode ->getArgs ()[0 ]->value );
9881038
989- if (count ($ exprNode ->getArgs ()) === 1 ) {
990- $ isNormalCount = TrinaryLogic::createYes ();
991- } else {
992- $ mode = $ scope ->getType ($ exprNode ->getArgs ()[1 ]->value );
993- $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->or ($ argType ->getIterableValueType ()->isArray ()->negate ());
994- }
995-
996- if (
997- $ isNormalCount ->yes ()
998- && $ argType instanceof UnionType
999- ) {
1000- $ result = [];
1001- foreach ($ argType ->getTypes () as $ innerType ) {
1002- $ arraySize = $ innerType ->getArraySize ();
1003- $ isSize = $ constantType ->isSuperTypeOf ($ arraySize );
1004- if ($ context ->truthy ()) {
1005- if ($ isSize ->no ()) {
1006- continue ;
1007- }
1008- }
1009- if ($ context ->falsey ()) {
1010- if (!$ isSize ->yes ()) {
1011- continue ;
1012- }
1013- }
1014-
1015- $ result [] = $ innerType ;
1039+ if ($ argType instanceof UnionType) {
1040+ $ narrowed = $ this ->narrowUnionByArraySize ($ exprNode , $ argType , $ constantType , $ context , $ scope , $ rootExpr );
1041+ if ($ narrowed !== null ) {
1042+ return $ narrowed ;
10161043 }
1017-
1018- return $ this ->create ($ exprNode ->getArgs ()[0 ]->value , TypeCombinator::union (...$ result ), $ context , false , $ scope , $ rootExpr );
10191044 }
10201045
10211046 if ($ context ->truthy () || $ constantType ->getValue () === 0 ) {
@@ -1025,6 +1050,13 @@ private function specifyTypesForConstantBinaryExpression(
10251050 }
10261051
10271052 if ($ argType ->isArray ()->yes ()) {
1053+ if (count ($ exprNode ->getArgs ()) === 1 ) {
1054+ $ isNormalCount = TrinaryLogic::createYes ();
1055+ } else {
1056+ $ mode = $ scope ->getType ($ exprNode ->getArgs ()[1 ]->value );
1057+ $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->or ($ argType ->getIterableValueType ()->isArray ()->negate ());
1058+ }
1059+
10281060 $ funcTypes = $ this ->create ($ exprNode , $ constantType , $ context , false , $ scope , $ rootExpr );
10291061 if ($ isNormalCount ->yes () && $ argType ->isList ()->yes () && $ context ->truthy () && $ constantType ->getValue () < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT ) {
10301062 $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
0 commit comments