diff --git a/src/Psalm/Checker/Statements/Expression/CallChecker.php b/src/Psalm/Checker/Statements/Expression/CallChecker.php index 0b9196ef2..df8523de2 100644 --- a/src/Psalm/Checker/Statements/Expression/CallChecker.php +++ b/src/Psalm/Checker/Statements/Expression/CallChecker.php @@ -1638,12 +1638,18 @@ class CallChecker $array_arg = isset($arg->value) ? $arg->value : null; - $array_arg_types[] = $array_arg + /** @var ObjectLike|TArray|null */ + $array_arg_type = $array_arg && isset($array_arg->inferredType) && isset($array_arg->inferredType->types['array']) - && $array_arg->inferredType->types['array'] instanceof Type\Atomic\TArray ? $array_arg->inferredType->types['array'] : null; + + if ($array_arg_type instanceof ObjectLike) { + $array_arg_type = new TArray([Type::getString(), $array_arg_type->getGenericTypeParam()]); + } + + $array_arg_types[] = $array_arg_type; } /** @var PhpParser\Node\Arg */ @@ -1688,6 +1694,11 @@ class CallChecker } } + // abandon attempt to validate closure params if we have an extra arg for ARRAY_FILTER + if ($method_id === 'array_filter' && count($args) > 2) { + continue; + } + $closure_params = $closure_type->params; $i = 0; diff --git a/tests/FunctionCallTest.php b/tests/FunctionCallTest.php index dd956f5c0..d830ec22f 100644 --- a/tests/FunctionCallTest.php +++ b/tests/FunctionCallTest.php @@ -462,6 +462,22 @@ class FunctionCallTest extends PHPUnit_Framework_TestCase $stmts = self::$parser->parse(' 5, "b" => 12, "c" => null], function(?int $val, string $key) : bool { return true; }, ARRAY_FILTER_USE_BOTH); $g = array_filter(["a" => 5, "b" => 12, "c" => null], function(string $val) : bool { return true; }, ARRAY_FILTER_USE_KEY); + + $bar = "bar"; + + $foo = [ + $bar => function () : string { + return "baz"; + }, + ]; + + $foo = array_filter( + $foo, + function (string $key) : bool { + return $key === "bar"; + }, + ARRAY_FILTER_USE_KEY + ); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); @@ -472,6 +488,36 @@ class FunctionCallTest extends PHPUnit_Framework_TestCase } } + /** + * @return void + */ + public function testArrayFilterUseKey() + { + if (version_compare((string)phpversion(), '5.6.0', '>=')) { + $stmts = self::$parser->parse(' function () : string { + return "baz"; + }, + ]; + + $foo = array_filter( + $foo, + function (string $key) : bool { + return $key === "bar"; + }, + ARRAY_FILTER_USE_KEY + ); + '); + + $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); + $context = new Context(); + $file_checker->visitAndAnalyzeMethods($context); + } + } + /** * @return void */