@@ -664,11 +664,85 @@ public function specifyTypesInCondition(
664
664
if (!$ scope instanceof MutatingScope) {
665
665
throw new ShouldNotHappenException ();
666
666
}
667
+
667
668
if ($ context ->null ()) {
668
- return $ this ->specifyTypesInCondition ($ scope ->exitFirstLevelStatements (), $ expr ->expr , $ context )->setRootExpr ($ expr );
669
+ $ specifiedTypes = $ this ->specifyTypesInCondition ($ scope ->exitFirstLevelStatements (), $ expr ->expr , $ context )->setRootExpr ($ expr );
670
+
671
+ // infer $arr[$key] after $key = array_key_first/last($arr)
672
+ if (
673
+ $ expr ->expr instanceof FuncCall
674
+ && $ expr ->expr ->name instanceof Name
675
+ && in_array ($ expr ->expr ->name ->toLowerString (), ['array_key_first ' , 'array_key_last ' ], true )
676
+ && count ($ expr ->expr ->getArgs ()) >= 1
677
+ ) {
678
+ $ arrayArg = $ expr ->expr ->getArgs ()[0 ]->value ;
679
+ $ arrayType = $ scope ->getType ($ arrayArg );
680
+ if (
681
+ $ arrayType ->isArray ()->yes ()
682
+ && $ arrayType ->isIterableAtLeastOnce ()->yes ()
683
+ ) {
684
+ $ dimFetch = new ArrayDimFetch ($ arrayArg , $ expr ->var );
685
+ $ iterableValueType = $ expr ->expr ->name ->toLowerString () === 'array_key_first '
686
+ ? $ arrayType ->getFirstIterableValueType ()
687
+ : $ arrayType ->getLastIterableValueType ();
688
+
689
+ return $ specifiedTypes ->unionWith (
690
+ $ this ->create ($ dimFetch , $ iterableValueType , TypeSpecifierContext::createTrue (), $ scope ),
691
+ );
692
+ }
693
+ }
694
+
695
+ // infer $list[$count] after $count = count($list) - 1
696
+ if (
697
+ $ expr ->expr instanceof Expr \BinaryOp \Minus
698
+ && $ expr ->expr ->left instanceof FuncCall
699
+ && $ expr ->expr ->left ->name instanceof Name
700
+ && in_array ($ expr ->expr ->left ->name ->toLowerString (), ['count ' , 'sizeof ' ], true )
701
+ && count ($ expr ->expr ->left ->getArgs ()) >= 1
702
+ && $ expr ->expr ->right instanceof Node \Scalar \Int_
703
+ && $ expr ->expr ->right ->value === 1
704
+ ) {
705
+ $ arrayArg = $ expr ->expr ->left ->getArgs ()[0 ]->value ;
706
+ $ arrayType = $ scope ->getType ($ arrayArg );
707
+ if (
708
+ $ arrayType ->isList ()->yes ()
709
+ && $ arrayType ->isIterableAtLeastOnce ()->yes ()
710
+ ) {
711
+ $ dimFetch = new ArrayDimFetch ($ arrayArg , $ expr ->var );
712
+
713
+ return $ specifiedTypes ->unionWith (
714
+ $ this ->create ($ dimFetch , $ arrayType ->getLastIterableValueType (), TypeSpecifierContext::createTrue (), $ scope ),
715
+ );
716
+ }
717
+ }
718
+
719
+ return $ specifiedTypes ;
669
720
}
670
721
671
- return $ this ->specifyTypesInCondition ($ scope ->exitFirstLevelStatements (), $ expr ->var , $ context )->setRootExpr ($ expr );
722
+ $ specifiedTypes = $ this ->specifyTypesInCondition ($ scope ->exitFirstLevelStatements (), $ expr ->var , $ context )->setRootExpr ($ expr );
723
+
724
+ if ($ context ->true ()) {
725
+ // infer $arr[$key] after $key = array_search($needle, $arr)
726
+ if (
727
+ $ expr ->expr instanceof FuncCall
728
+ && $ expr ->expr ->name instanceof Name
729
+ && $ expr ->expr ->name ->toLowerString () === 'array_search '
730
+ && count ($ expr ->expr ->getArgs ()) >= 2
731
+ ) {
732
+ $ arrayArg = $ expr ->expr ->getArgs ()[1 ]->value ;
733
+ $ arrayType = $ scope ->getType ($ arrayArg );
734
+
735
+ if ($ arrayType ->isArray ()->yes ()) {
736
+ $ dimFetch = new ArrayDimFetch ($ arrayArg , $ expr ->var );
737
+ $ iterableValueType = $ arrayType ->getIterableValueType ();
738
+
739
+ return $ specifiedTypes ->unionWith (
740
+ $ this ->create ($ dimFetch , $ iterableValueType , TypeSpecifierContext::createTrue (), $ scope ),
741
+ );
742
+ }
743
+ }
744
+ }
745
+ return $ specifiedTypes ;
672
746
} elseif (
673
747
$ expr instanceof Expr \Isset_
674
748
&& count ($ expr ->vars ) > 0
0 commit comments