From 138cabd75aa923eff9f320675e905dafc2c3dc49 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 11 Nov 2020 19:39:06 +0100 Subject: [PATCH] Support ARRAY_FILTER_USE_KEY and ARRAY_FILTER_USE_BOTH --- src/Analyser/NodeScopeResolver.php | 25 ++++++++-- .../Analyser/NodeScopeResolverTest.php | 6 +++ tests/PHPStan/Analyser/data/bug-3132.php | 48 +++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/bug-3132.php diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 9f2e9fe0d9..55f9673fe2 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -2411,9 +2411,28 @@ private function processArgs( && $calleeReflection->getName() === 'array_filter' && isset($args[0]) ) { - $parameterType = new CallableType([ - new DummyParameter('item', $scope->getType($args[0]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null), - ], new MixedType(), false); + if (isset($args[2])) { + $mode = $scope->getType($args[2]->value); + if ($mode instanceof ConstantIntegerType) { + if ($mode->getValue() === ARRAY_FILTER_USE_KEY) { + $arrayFilterParameters = [ + new DummyParameter('key', $scope->getType($args[0]->value)->getIterableKeyType(), false, PassedByReference::createNo(), false, null), + ]; + } elseif ($mode->getValue() === ARRAY_FILTER_USE_BOTH) { + $arrayFilterParameters = [ + new DummyParameter('item', $scope->getType($args[0]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null), + new DummyParameter('key', $scope->getType($args[0]->value)->getIterableKeyType(), false, PassedByReference::createNo(), false, null), + ]; + } + } + } + $parameterType = new CallableType( + $arrayFilterParameters ?? [ + new DummyParameter('item', $scope->getType($args[0]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null), + ], + new MixedType(), + false + ); } } } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 64fa2f38f1..2a0b0df629 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -10322,6 +10322,11 @@ public function dataBug2733(): array return $this->gatherAssertTypes(__DIR__ . '/data/bug-2733.php'); } + public function dataBug3132(): array + { + return $this->gatherAssertTypes(__DIR__ . '/data/bug-3132.php'); + } + /** * @param string $file * @return array @@ -10499,6 +10504,7 @@ private function gatherAssertTypes(string $file): array * @dataProvider dataCastToNumericString * @dataProvider dataBug2539 * @dataProvider dataBug2733 + * @dataProvider dataBug3132 * @param string $assertType * @param string $file * @param mixed ...$args diff --git a/tests/PHPStan/Analyser/data/bug-3132.php b/tests/PHPStan/Analyser/data/bug-3132.php new file mode 100644 index 0000000000..51e97e2afa --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-3132.php @@ -0,0 +1,48 @@ + $objects + * + * @return array + */ + function filter(array $objects) : array + { + return array_filter($objects, static function ($key) { + assertType('string', $key); + }, ARRAY_FILTER_USE_KEY); + } + + /** + * @param array $objects + * + * @return array + */ + function bar(array $objects) : array + { + return array_filter($objects, static function ($val) { + assertType('object', $val); + }); + } + + /** + * @param array $objects + * + * @return array + */ + function baz(array $objects) : array + { + return array_filter($objects, static function ($val, $key) { + assertType('string', $key); + assertType('object', $val); + }, ARRAY_FILTER_USE_BOTH); + } + +}