diff --git a/src/Psalm/Checker/Statements/Block/ForeachChecker.php b/src/Psalm/Checker/Statements/Block/ForeachChecker.php index f5f17d271..ef49d8849 100644 --- a/src/Psalm/Checker/Statements/Block/ForeachChecker.php +++ b/src/Psalm/Checker/Statements/Block/ForeachChecker.php @@ -5,6 +5,7 @@ use PhpParser; use Psalm\Checker\ClassChecker; use Psalm\Checker\ClassLikeChecker; use Psalm\Checker\CommentChecker; +use Psalm\Checker\InterfaceChecker; use Psalm\Checker\MethodChecker; use Psalm\Checker\Statements\Expression\AssignmentChecker; use Psalm\Checker\Statements\ExpressionChecker; @@ -178,7 +179,15 @@ class ForeachChecker $project_checker, $iterator_type->value, 'Iterator' - )) { + ) || + (InterfaceChecker::interfaceExists($project_checker, $iterator_type->value) + && InterfaceChecker::interfaceExtends( + $project_checker, + $iterator_type->value, + 'Iterator' + ) + ) + ) { $iterator_method = $iterator_type->value . '::current'; $iterator_class_type = MethodChecker::getMethodReturnType($project_checker, $iterator_method); @@ -202,11 +211,20 @@ class ForeachChecker $project_checker, $iterator_type->value, 'Traversable' - )) { + ) || + (InterfaceChecker::interfaceExists($project_checker, $iterator_type->value) + && InterfaceChecker::interfaceExtends( + $project_checker, + $iterator_type->value, + 'Traversable' + ) + ) + ) { // @todo try and get value type } elseif (!in_array( strtolower($iterator_type->value), - ['iterator', 'iterable', 'traversable'], true + ['iterator', 'iterable', 'traversable'], + true )) { if (IssueBuffer::accepts( new RawObjectIteration( diff --git a/tests/ForeachTest.php b/tests/ForeachTest.php index 72a6fb448..5556e4cc5 100644 --- a/tests/ForeachTest.php +++ b/tests/ForeachTest.php @@ -4,6 +4,43 @@ namespace Psalm\Tests; class ForeachTest extends TestCase { use Traits\FileCheckerInvalidCodeParseTestTrait; + use Traits\FileCheckerValidCodeParseTestTrait; + + /** + * @return array + */ + public function providerFileCheckerValidCodeParse() + { + return [ + 'iteratorAggregateIteration' => [ + ' [], + 'error_levels' => [ + 'MixedAssignment', 'UndefinedThisPropertyAssignment', + ], + ], + ]; + } /** * @return array