@@ -351,6 +351,35 @@ public function specifyTypesInCondition(
351351 }
352352 }
353353
354+ // infer $list[$index] after $zeroOrMore < count($list) - N
355+ // infer $list[$index] after $zeroOrMore <= count($list) - N
356+ if (
357+ $ context ->true ()
358+ && $ expr ->right instanceof Expr \BinaryOp \Minus
359+ && $ expr ->right ->left instanceof FuncCall
360+ && $ expr ->right ->left ->name instanceof Name
361+ && in_array (strtolower ((string ) $ expr ->right ->left ->name ), ['count ' , 'sizeof ' ], true )
362+ && count ($ expr ->right ->left ->getArgs ()) >= 1
363+ // constant offsets are handled via HasOffsetType/HasOffsetValueType
364+ && !$ leftType instanceof ConstantIntegerType
365+ && $ leftType ->isInteger ()->yes ()
366+ && IntegerRangeType::fromInterval (0 , null )->isSuperTypeOf ($ leftType )->yes ()
367+ ) {
368+ $ countArgType = $ scope ->getType ($ expr ->right ->left ->getArgs ()[0 ]->value );
369+ $ subtractedType = $ scope ->getType ($ expr ->right ->right );
370+ if (
371+ $ countArgType ->isList ()->yes ()
372+ && $ this ->isNormalCountCall ($ expr ->right ->left , $ countArgType , $ scope )->yes ()
373+ && IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ subtractedType )->yes ()
374+ ) {
375+ $ arrayArg = $ expr ->right ->left ->getArgs ()[0 ]->value ;
376+ $ dimFetch = new ArrayDimFetch ($ arrayArg , $ expr ->left );
377+ $ result = $ result ->unionWith (
378+ $ this ->create ($ dimFetch , $ countArgType ->getIterableValueType (), TypeSpecifierContext::createTrue (), $ scope )->setRootExpr ($ expr ),
379+ );
380+ }
381+ }
382+
354383 if (
355384 !$ context ->null ()
356385 && $ expr ->right instanceof FuncCall
@@ -1211,6 +1240,16 @@ public function specifyTypesInCondition(
12111240 return (new SpecifiedTypes ([], []))->setRootExpr ($ expr );
12121241 }
12131242
1243+ private function isNormalCountCall (FuncCall $ countFuncCall , Type $ typeToCount , Scope $ scope ): TrinaryLogic
1244+ {
1245+ if (count ($ countFuncCall ->getArgs ()) === 1 ) {
1246+ return TrinaryLogic::createYes ();
1247+ }
1248+
1249+ $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
1250+ return (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->result ->or ($ typeToCount ->getIterableValueType ()->isArray ()->negate ());
1251+ }
1252+
12141253 private function specifyTypesForCountFuncCall (
12151254 FuncCall $ countFuncCall ,
12161255 Type $ type ,
@@ -1220,18 +1259,11 @@ private function specifyTypesForCountFuncCall(
12201259 Expr $ rootExpr ,
12211260 ): ?SpecifiedTypes
12221261 {
1223- if (count ($ countFuncCall ->getArgs ()) === 1 ) {
1224- $ isNormalCount = TrinaryLogic::createYes ();
1225- } else {
1226- $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
1227- $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->result ->or ($ type ->getIterableValueType ()->isArray ()->negate ());
1228- }
1229-
12301262 $ isConstantArray = $ type ->isConstantArray ();
12311263 $ isList = $ type ->isList ();
12321264 $ oneOrMore = IntegerRangeType::fromInterval (1 , null );
12331265 if (
1234- !$ isNormalCount ->yes ()
1266+ !$ this -> isNormalCountCall ( $ countFuncCall , $ type , $ scope ) ->yes ()
12351267 || (!$ isConstantArray ->yes () && !$ isList ->yes ())
12361268 || !$ oneOrMore ->isSuperTypeOf ($ sizeType )->yes ()
12371269 || $ sizeType ->isSuperTypeOf ($ type ->getArraySize ())->yes ()
0 commit comments