From 9b634cf5b682d30275f6a1d64160cf7ee22e5b8c Mon Sep 17 00:00:00 2001 From: Brad <28307684+mad-briller@users.noreply.github.com> Date: Sat, 30 Jul 2022 14:28:15 +0100 Subject: [PATCH 1/6] Added stub files and tests for: array_diff_uassoc array_diff_ukey array_intersect_uassoc array_intersect_ukey array_udiff_assoc array_udiff_uassoc array_uintersect_assoc array_uintersect_uassoc array_uintersect --- stubs/arrayFunctions.stub | 141 ++++++++++++++++ .../CallToFunctionParametersRuleTest.php | 151 ++++++++++++++++++ .../Functions/data/array_diff_uassoc.php | 33 ++++ .../Rules/Functions/data/array_diff_ukey.php | 33 ++++ .../Functions/data/array_intersect_uassoc.php | 33 ++++ .../Functions/data/array_intersect_ukey.php | 33 ++++ .../Functions/data/array_udiff_assoc.php | 33 ++++ .../Functions/data/array_udiff_uassoc.php | 45 ++++++ .../Rules/Functions/data/array_uintersect.php | 33 ++++ .../Functions/data/array_uintersect_assoc.php | 33 ++++ .../data/array_uintersect_uassoc.php | 45 ++++++ 11 files changed, 613 insertions(+) create mode 100644 tests/PHPStan/Rules/Functions/data/array_diff_uassoc.php create mode 100644 tests/PHPStan/Rules/Functions/data/array_diff_ukey.php create mode 100644 tests/PHPStan/Rules/Functions/data/array_intersect_uassoc.php create mode 100644 tests/PHPStan/Rules/Functions/data/array_intersect_ukey.php create mode 100644 tests/PHPStan/Rules/Functions/data/array_udiff_assoc.php create mode 100644 tests/PHPStan/Rules/Functions/data/array_udiff_uassoc.php create mode 100644 tests/PHPStan/Rules/Functions/data/array_uintersect.php create mode 100644 tests/PHPStan/Rules/Functions/data/array_uintersect_assoc.php create mode 100644 tests/PHPStan/Rules/Functions/data/array_uintersect_uassoc.php diff --git a/stubs/arrayFunctions.stub b/stubs/arrayFunctions.stub index 25c73249ec..d85ccd4eb3 100644 --- a/stubs/arrayFunctions.stub +++ b/stubs/arrayFunctions.stub @@ -67,3 +67,144 @@ function array_udiff( * @return ($value is __always-list ? true : false) */ function array_is_list(array $value): bool {} + +/** + * @template TK of mixed + * @template TV of mixed + * + * @param array $one + * @param array $two + * @param callable(TK, TK): int<-1, 1> $three + * @return array + */ +function array_diff_uassoc( + array $one, + array $two, + callable $three +): array {} + +/** + * @template TK of mixed + * @template TV of mixed + * + * @param array $one + * @param array $two + * @param callable(TK, TK): int<-1, 1> $three + * @return array + */ +function array_diff_ukey( + array $one, + array $two, + callable $three +): array {} + +/** + * @template TK of mixed + * @template TV of mixed + * + * @param array $one + * @param array $two + * @param callable(TK, TK): int<-1, 1> $three + * @return array + */ +function array_intersect_uassoc( + array $one, + array $two, + callable $three +): array {} + +/** + * @template TK of mixed + * @template TV of mixed + * + * @param array $one + * @param array $two + * @param callable(TK, TK): int<-1, 1> $three + * + * @return array + */ +function array_intersect_ukey( + array $one, + array $two, + callable $three +): array {} + +/** + * @template TK of mixed + * @template TV of mixed + * + * @param array $one + * @param array $two + * @param callable(TV, TV): int<-1, 1> $three + * + * @return array + */ +function array_udiff_assoc( + array $one, + array $two, + callable $three +): array {} + +/** + * @template TK of mixed + * @template TV of mixed + * + * @param array $one + * @param array $two + * @param callable(TV, TV): int<-1, 1> $three + * @param callable(TK, TK): int<-1, 1> $four + * @return array + */ +function array_udiff_uassoc( + array $one, + array $two, + callable $three, + callable $four +): array {} + +/** + * @template TK of mixed + * @template TV of mixed + * + * @param array $one + * @param array $two + * @param callable(TV, TV): int<-1, 1> $three + * @return array + */ +function array_uintersect_assoc( + array $one, + array $two, + callable $three, +): array {} + +/** + * @template TK of mixed + * @template TV of mixed + * + * @param array $one + * @param array $two + * @param callable(TV, TV): int<-1, 1> $three + * @param callable(TK, TK): int<-1, 1> $four + * @return array + */ +function array_uintersect_uassoc( + array $one, + array $two, + callable $three, + callable $four +): array {} + +/** + * @template TK of mixed + * @template TV of mixed + * + * @param array $one + * @param array $two + * @param callable(TV, TV): int<-1, 1> $three + * @return array + */ +function array_uintersect( + array $one, + array $two, + callable $three, +): array {} diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index a47624e8e8..b86b09ddfd 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1716,6 +1716,157 @@ public function testCountArrayShift(): void $this->analyse([__DIR__ . '/data/count-array-shift.php'], $errors); } + public function testArrayDiffUassoc(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/array_diff_uassoc.php'], [ + [ + 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 22, + ], + [ + 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 30, + ], + ]); + } + + public function testArrayDiffUkey(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/array_diff_ukey.php'], [ + [ + 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 22, + ], + [ + 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 30, + ], + ]); + } + + public function testArrayIntersectUassoc(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/array_intersect_uassoc.php'], [ + [ + 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 22, + ], + [ + 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 30, + ], + ]); + } + + public function testArrayIntersectUkey(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/array_intersect_ukey.php'], [ + [ + 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 22, + ], + [ + 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 30, + ], + ]); + } + + public function testArrayUdiffAssoc(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/array_udiff_assoc.php'], [ + [ + 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 22, + ], + [ + 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 30, + ], + ]); + } + + public function testArrayUdiffUsssoc(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/array_udiff_uassoc.php'], [ + [ + 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 28, + ], + [ + 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 31, + ], + [ + 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 39, + ], + [ + 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 42, + ], + ]); + } + + public function testArrayUintersectAssoc(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/array_uintersect_assoc.php'], [ + [ + 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 22, + ], + [ + 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 30, + ], + ]); + } + + public function testArrayUintersectUassoc(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/array_uintersect_uassoc.php'], [ + [ + 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 28, + ], + [ + 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 31, + ], + [ + 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 39, + ], + [ + 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 42, + ], + ]); + } + + public function testArrayUintersect(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/array_uintersect.php'], [ + [ + 'Parameter #3 $data_compare_func of function array_uintersect expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 22, + ], + [ + 'Parameter #3 $data_compare_func of function array_uintersect expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 30, + ], + ]); + } + public function testNoNamedArguments(): void { if (PHP_VERSION_ID < 80000) { diff --git a/tests/PHPStan/Rules/Functions/data/array_diff_uassoc.php b/tests/PHPStan/Rules/Functions/data/array_diff_uassoc.php new file mode 100644 index 0000000000..0884cd24fd --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array_diff_uassoc.php @@ -0,0 +1,33 @@ + 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (string $a, string $b): int { + return $a <=> $b; + } +); + +array_diff_uassoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_diff_uassoc( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_diff_uassoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (string $a, string $b): int { + return $a <=> $b; + } +); diff --git a/tests/PHPStan/Rules/Functions/data/array_diff_ukey.php b/tests/PHPStan/Rules/Functions/data/array_diff_ukey.php new file mode 100644 index 0000000000..23a7303a9f --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array_diff_ukey.php @@ -0,0 +1,33 @@ + 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (string $a, string $b): int { + return $a <=> $b; + } +); + +array_diff_ukey( + [1, 2, 3], + [1, 2, 4, 5], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_diff_ukey( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_diff_ukey( + [1, 2, 3], + [1, 2, 4, 5], + static function (string $a, string $b): int { + return $a <=> $b; + } +); diff --git a/tests/PHPStan/Rules/Functions/data/array_intersect_uassoc.php b/tests/PHPStan/Rules/Functions/data/array_intersect_uassoc.php new file mode 100644 index 0000000000..395d009139 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array_intersect_uassoc.php @@ -0,0 +1,33 @@ + 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (string $a, string $b): int { + return $a <=> $b; + } +); + +array_intersect_uassoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_intersect_uassoc( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_intersect_uassoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (string $a, string $b): int { + return $a <=> $b; + } +); diff --git a/tests/PHPStan/Rules/Functions/data/array_intersect_ukey.php b/tests/PHPStan/Rules/Functions/data/array_intersect_ukey.php new file mode 100644 index 0000000000..656999fb02 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array_intersect_ukey.php @@ -0,0 +1,33 @@ + 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (string $a, string $b): int { + return $a <=> $b; + } +); + +array_intersect_ukey( + [1, 2, 3], + [1, 2, 4, 5], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_intersect_ukey( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_intersect_ukey( + [1, 2, 3], + [1, 2, 4, 5], + static function (string $a, string $b): int { + return $a <=> $b; + } +); diff --git a/tests/PHPStan/Rules/Functions/data/array_udiff_assoc.php b/tests/PHPStan/Rules/Functions/data/array_udiff_assoc.php new file mode 100644 index 0000000000..6ce8d8cbdf --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array_udiff_assoc.php @@ -0,0 +1,33 @@ + 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_udiff_assoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_udiff_assoc( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + static function (string $a, string $b): int { + return $a <=> $b; + } +); + +array_udiff_assoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (string $a, string $b): int { + return $a <=> $b; + } +); diff --git a/tests/PHPStan/Rules/Functions/data/array_udiff_uassoc.php b/tests/PHPStan/Rules/Functions/data/array_udiff_uassoc.php new file mode 100644 index 0000000000..edbea3ecf3 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array_udiff_uassoc.php @@ -0,0 +1,45 @@ + 'a', 'b' => 'b'], + ['c' => 'c', 'd' => 'd'], + static function (string $a, string $b): int { + return $a <=> $b; + }, + static function (string $a, string $b): int { + return $a <=> $b; + } +); + +array_udiff_uassoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (int $a, int $b): int { + return $a <=> $b; + }, + static function (int $a, int $b): int { + return $a <=> $b; + }, +); + +array_udiff_uassoc( + ['a' => 'a', 'b' => 'b'], + ['c' => 'c', 'd' => 'd'], + static function (int $a, int $b): int { + return $a <=> $b; + }, + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_udiff_uassoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (string $a, string $b): int { + return $a <=> $b; + }, + static function (string $a, string $b): int { + return $a <=> $b; + } +); diff --git a/tests/PHPStan/Rules/Functions/data/array_uintersect.php b/tests/PHPStan/Rules/Functions/data/array_uintersect.php new file mode 100644 index 0000000000..193ee0a3a7 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array_uintersect.php @@ -0,0 +1,33 @@ + $b; + } +); + +array_uintersect( + [1, 2, 3], + [1, 2, 3, 4], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_uintersect( + ['a', 'b'], + ['c', 'd'], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_uintersect( + [1, 2, 3], + [1, 2, 3, 4], + static function (string $a, string $b): int { + return $a <=> $b; + } +); diff --git a/tests/PHPStan/Rules/Functions/data/array_uintersect_assoc.php b/tests/PHPStan/Rules/Functions/data/array_uintersect_assoc.php new file mode 100644 index 0000000000..328d8afb6b --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array_uintersect_assoc.php @@ -0,0 +1,33 @@ + 'a', 'b' => 'b'], + ['c' => 'c', 'd' => 'd'], + static function (string $a, string $b): int { + return $a <=> $b; + } +); + +array_uintersect_assoc( + [1, 2, 3], + [1, 2, 3, 4], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_uintersect_assoc( + ['a' => 'a', 'b' => 'b'], + ['c' => 'c', 'd' => 'd'], + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_uintersect_assoc( + [1, 2, 3], + [1, 2, 3, 4], + static function (string $a, string $b): int { + return $a <=> $b; + } +); diff --git a/tests/PHPStan/Rules/Functions/data/array_uintersect_uassoc.php b/tests/PHPStan/Rules/Functions/data/array_uintersect_uassoc.php new file mode 100644 index 0000000000..d287aa5809 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array_uintersect_uassoc.php @@ -0,0 +1,45 @@ + 'a', 'b' => 'b'], + ['c' => 'c', 'd' => 'd'], + static function (string $a, string $b): int { + return $a <=> $b; + }, + static function (string $a, string $b): int { + return $a <=> $b; + } +); + +array_uintersect_uassoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (int $a, int $b): int { + return $a <=> $b; + }, + static function (int $a, int $b): int { + return $a <=> $b; + }, +); + +array_uintersect_uassoc( + ['a' => 'a', 'b' => 'b'], + ['c' => 'c', 'd' => 'd'], + static function (int $a, int $b): int { + return $a <=> $b; + }, + static function (int $a, int $b): int { + return $a <=> $b; + } +); + +array_uintersect_uassoc( + [1, 2, 3], + [1, 2, 4, 5], + static function (string $a, string $b): int { + return $a <=> $b; + }, + static function (string $a, string $b): int { + return $a <=> $b; + } +); From 1ff2ba3df51062d3cdbc1d71670c6a757763eaa1 Mon Sep 17 00:00:00 2001 From: schlndh Date: Fri, 2 Aug 2024 13:37:04 +0200 Subject: [PATCH 2/6] bound TK of array_intersect_ukey etc to array-key --- src/Type/TypehintHelper.php | 7 ++- stubs/arrayFunctions.stub | 18 ++++---- .../CallToFunctionParametersRuleTest.php | 46 +++++++++---------- .../Functions/data/array_diff_uassoc.php | 2 +- .../Rules/Functions/data/array_diff_ukey.php | 2 +- .../Functions/data/array_intersect_uassoc.php | 2 +- .../Functions/data/array_intersect_ukey.php | 2 +- .../Functions/data/array_udiff_assoc.php | 2 +- .../Functions/data/array_udiff_uassoc.php | 2 +- .../Rules/Functions/data/array_uintersect.php | 2 +- .../Functions/data/array_uintersect_assoc.php | 2 +- .../data/array_uintersect_uassoc.php | 2 +- 12 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/Type/TypehintHelper.php b/src/Type/TypehintHelper.php index 8f9f5616f3..55e4843f36 100644 --- a/src/Type/TypehintHelper.php +++ b/src/Type/TypehintHelper.php @@ -201,8 +201,11 @@ public static function decideType( } if ( - (!$phpDocType instanceof NeverType || ($type instanceof MixedType && !$type->isExplicitMixed())) - && $type->isSuperTypeOf(TemplateTypeHelper::resolveToBounds($phpDocType))->yes() + ($type->isCallable()->yes() && $phpDocType->isCallable()->yes()) + || ( + (!$phpDocType instanceof NeverType || ($type instanceof MixedType && !$type->isExplicitMixed())) + && $type->isSuperTypeOf(TemplateTypeHelper::resolveToBounds($phpDocType))->yes() + ) ) { $resultType = $phpDocType; } else { diff --git a/stubs/arrayFunctions.stub b/stubs/arrayFunctions.stub index d85ccd4eb3..db6e3be902 100644 --- a/stubs/arrayFunctions.stub +++ b/stubs/arrayFunctions.stub @@ -69,7 +69,7 @@ function array_udiff( function array_is_list(array $value): bool {} /** - * @template TK of mixed + * @template TK of array-key * @template TV of mixed * * @param array $one @@ -84,7 +84,7 @@ function array_diff_uassoc( ): array {} /** - * @template TK of mixed + * @template TK of array-key * @template TV of mixed * * @param array $one @@ -99,7 +99,7 @@ function array_diff_ukey( ): array {} /** - * @template TK of mixed + * @template TK of array-key * @template TV of mixed * * @param array $one @@ -114,7 +114,7 @@ function array_intersect_uassoc( ): array {} /** - * @template TK of mixed + * @template TK of array-key * @template TV of mixed * * @param array $one @@ -130,7 +130,7 @@ function array_intersect_ukey( ): array {} /** - * @template TK of mixed + * @template TK of array-key * @template TV of mixed * * @param array $one @@ -146,7 +146,7 @@ function array_udiff_assoc( ): array {} /** - * @template TK of mixed + * @template TK of array-key * @template TV of mixed * * @param array $one @@ -163,7 +163,7 @@ function array_udiff_uassoc( ): array {} /** - * @template TK of mixed + * @template TK of array-key * @template TV of mixed * * @param array $one @@ -178,7 +178,7 @@ function array_uintersect_assoc( ): array {} /** - * @template TK of mixed + * @template TK of array-key * @template TV of mixed * * @param array $one @@ -195,7 +195,7 @@ function array_uintersect_uassoc( ): array {} /** - * @template TK of mixed + * @template TK of array-key * @template TV of mixed * * @param array $one diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index b86b09ddfd..d368a11383 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1721,11 +1721,11 @@ public function testArrayDiffUassoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_diff_uassoc.php'], [ [ - 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1736,11 +1736,11 @@ public function testArrayDiffUkey(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_diff_ukey.php'], [ [ - 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1751,11 +1751,11 @@ public function testArrayIntersectUassoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_intersect_uassoc.php'], [ [ - 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1766,11 +1766,11 @@ public function testArrayIntersectUkey(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_intersect_ukey.php'], [ [ - 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1781,34 +1781,34 @@ public function testArrayUdiffAssoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_udiff_assoc.php'], [ [ - 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(1|2, 1|2): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(1|2|3|4|5, 1|2|3|4|5): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 30, ], ]); } - public function testArrayUdiffUsssoc(): void + public function testArrayUdiffUasssoc(): void { $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_udiff_uassoc.php'], [ [ - 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 28, ], [ - 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 31, ], [ - 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(1|2|3|4|5, 1|2|3|4|5): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 39, ], [ - 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 42, ], ]); @@ -1819,11 +1819,11 @@ public function testArrayUintersectAssoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_uintersect_assoc.php'], [ [ - 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(1|2|3|4, 1|2|3|4): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1834,19 +1834,19 @@ public function testArrayUintersectUassoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_uintersect_uassoc.php'], [ [ - 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 28, ], [ - 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 31, ], [ - 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(1|2|3|4|5, 1|2|3|4|5): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 39, ], [ - 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 42, ], ]); @@ -1857,11 +1857,11 @@ public function testArrayUintersect(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_uintersect.php'], [ [ - 'Parameter #3 $data_compare_func of function array_uintersect expects callable(string, string): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $data_compare_func of function array_uintersect expects callable(int, int): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect expects callable(1|2|3|4, 1|2|3|4): int<-1, 1>, Closure(string, string): int<-1, 1> given.', 30, ], ]); diff --git a/tests/PHPStan/Rules/Functions/data/array_diff_uassoc.php b/tests/PHPStan/Rules/Functions/data/array_diff_uassoc.php index 0884cd24fd..257fc17c85 100644 --- a/tests/PHPStan/Rules/Functions/data/array_diff_uassoc.php +++ b/tests/PHPStan/Rules/Functions/data/array_diff_uassoc.php @@ -1,4 +1,4 @@ - 1, 'b' => 2], diff --git a/tests/PHPStan/Rules/Functions/data/array_diff_ukey.php b/tests/PHPStan/Rules/Functions/data/array_diff_ukey.php index 23a7303a9f..8a98630a88 100644 --- a/tests/PHPStan/Rules/Functions/data/array_diff_ukey.php +++ b/tests/PHPStan/Rules/Functions/data/array_diff_ukey.php @@ -1,4 +1,4 @@ - 1, 'b' => 2], diff --git a/tests/PHPStan/Rules/Functions/data/array_intersect_uassoc.php b/tests/PHPStan/Rules/Functions/data/array_intersect_uassoc.php index 395d009139..9f8ba1bfa3 100644 --- a/tests/PHPStan/Rules/Functions/data/array_intersect_uassoc.php +++ b/tests/PHPStan/Rules/Functions/data/array_intersect_uassoc.php @@ -1,4 +1,4 @@ - 1, 'b' => 2], diff --git a/tests/PHPStan/Rules/Functions/data/array_intersect_ukey.php b/tests/PHPStan/Rules/Functions/data/array_intersect_ukey.php index 656999fb02..4d81086d24 100644 --- a/tests/PHPStan/Rules/Functions/data/array_intersect_ukey.php +++ b/tests/PHPStan/Rules/Functions/data/array_intersect_ukey.php @@ -1,4 +1,4 @@ - 1, 'b' => 2], diff --git a/tests/PHPStan/Rules/Functions/data/array_udiff_assoc.php b/tests/PHPStan/Rules/Functions/data/array_udiff_assoc.php index 6ce8d8cbdf..7827734e59 100644 --- a/tests/PHPStan/Rules/Functions/data/array_udiff_assoc.php +++ b/tests/PHPStan/Rules/Functions/data/array_udiff_assoc.php @@ -1,4 +1,4 @@ - 1, 'b' => 2], diff --git a/tests/PHPStan/Rules/Functions/data/array_udiff_uassoc.php b/tests/PHPStan/Rules/Functions/data/array_udiff_uassoc.php index edbea3ecf3..f4767621c2 100644 --- a/tests/PHPStan/Rules/Functions/data/array_udiff_uassoc.php +++ b/tests/PHPStan/Rules/Functions/data/array_udiff_uassoc.php @@ -1,4 +1,4 @@ - 'a', 'b' => 'b'], diff --git a/tests/PHPStan/Rules/Functions/data/array_uintersect.php b/tests/PHPStan/Rules/Functions/data/array_uintersect.php index 193ee0a3a7..dbe5307a82 100644 --- a/tests/PHPStan/Rules/Functions/data/array_uintersect.php +++ b/tests/PHPStan/Rules/Functions/data/array_uintersect.php @@ -1,4 +1,4 @@ - 'a', 'b' => 'b'], diff --git a/tests/PHPStan/Rules/Functions/data/array_uintersect_uassoc.php b/tests/PHPStan/Rules/Functions/data/array_uintersect_uassoc.php index d287aa5809..f079aec189 100644 --- a/tests/PHPStan/Rules/Functions/data/array_uintersect_uassoc.php +++ b/tests/PHPStan/Rules/Functions/data/array_uintersect_uassoc.php @@ -1,4 +1,4 @@ - 'a', 'b' => 'b'], From 2f3d0bb55b97200e40420dde1472f47130caeb51 Mon Sep 17 00:00:00 2001 From: schlndh Date: Fri, 2 Aug 2024 13:46:38 +0200 Subject: [PATCH 3/6] add test for issue 7707 --- .../CallToFunctionParametersRuleTest.php | 7 ++ .../PHPStan/Rules/Functions/data/bug-7707.php | 98 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 tests/PHPStan/Rules/Functions/data/bug-7707.php diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index d368a11383..198ee2d2c7 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1867,6 +1867,13 @@ public function testArrayUintersect(): void ]); } + public function testBug7707(): void + { + $this->checkExplicitMixed = true; + + $this->analyse([__DIR__ . '/data/bug-7707.php'], []); + } + public function testNoNamedArguments(): void { if (PHP_VERSION_ID < 80000) { diff --git a/tests/PHPStan/Rules/Functions/data/bug-7707.php b/tests/PHPStan/Rules/Functions/data/bug-7707.php new file mode 100644 index 0000000000..1fc7bbab6c --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-7707.php @@ -0,0 +1,98 @@ + 1, 'b' => 2], + ['c' => 1, 'd' => 2], + // keys comparison + static function (string $a, string $b): int { + return $a <=> $b; + } + )); + + var_dump(array_diff_ukey( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + // keys comparison + static function (string $a, string $b): int { + return $a <=> $b; + } + )); + + var_dump(array_intersect_uassoc( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + // keys comparison + static function (string $a, string $b): int { + return $a <=> $b; + } + )); + + var_dump(array_intersect_ukey( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + // keys comparison + static function (string $a, string $b): int { + return $a <=> $b; + } + )); + + var_dump(array_udiff_assoc( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + // values comparison + static function (int $a, int $b): int { + return $a <=> $b; + } + )); + + var_dump(array_udiff_uassoc( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + // values comparison + static function (int $a, int $b): int { + return $a <=> $b; + }, + // keys comparison + static function (string $a, string $b): int { + return $a <=> $b; + } + )); + + var_dump(array_uintersect_assoc( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + // values comparison + static function (int $a, int $b): int { + return $a <=> $b; + } + )); + + var_dump(array_uintersect_uassoc( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + // values comparison + static function (int $a, int $b): int { + return $a <=> $b; + }, + // keys comparison + static function (string $a, string $b): int { + return $a <=> $b; + } + )); + + var_dump(array_uintersect( + ['a' => 1, 'b' => 2], + ['c' => 1, 'd' => 2], + // values comparison + static function (int $a, int $b): int { + return $a <=> $b; + } + )); + } +} From c368b45587e451d0dbd025932787a4ff880f49f9 Mon Sep 17 00:00:00 2001 From: schlndh Date: Fri, 13 Sep 2024 12:32:16 +0200 Subject: [PATCH 4/6] allow callbacks in array intersect/diff to return int See issue 9697 --- stubs/arrayFunctions.stub | 22 +++++----- .../CallToFunctionParametersRuleTest.php | 44 +++++++++---------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/stubs/arrayFunctions.stub b/stubs/arrayFunctions.stub index db6e3be902..b17bf519eb 100644 --- a/stubs/arrayFunctions.stub +++ b/stubs/arrayFunctions.stub @@ -74,7 +74,7 @@ function array_is_list(array $value): bool {} * * @param array $one * @param array $two - * @param callable(TK, TK): int<-1, 1> $three + * @param callable(TK, TK): int $three * @return array */ function array_diff_uassoc( @@ -89,7 +89,7 @@ function array_diff_uassoc( * * @param array $one * @param array $two - * @param callable(TK, TK): int<-1, 1> $three + * @param callable(TK, TK): int $three * @return array */ function array_diff_ukey( @@ -104,7 +104,7 @@ function array_diff_ukey( * * @param array $one * @param array $two - * @param callable(TK, TK): int<-1, 1> $three + * @param callable(TK, TK): int $three * @return array */ function array_intersect_uassoc( @@ -119,7 +119,7 @@ function array_intersect_uassoc( * * @param array $one * @param array $two - * @param callable(TK, TK): int<-1, 1> $three + * @param callable(TK, TK): int $three * * @return array */ @@ -135,7 +135,7 @@ function array_intersect_ukey( * * @param array $one * @param array $two - * @param callable(TV, TV): int<-1, 1> $three + * @param callable(TV, TV): int $three * * @return array */ @@ -151,8 +151,8 @@ function array_udiff_assoc( * * @param array $one * @param array $two - * @param callable(TV, TV): int<-1, 1> $three - * @param callable(TK, TK): int<-1, 1> $four + * @param callable(TV, TV): int $three + * @param callable(TK, TK): int $four * @return array */ function array_udiff_uassoc( @@ -168,7 +168,7 @@ function array_udiff_uassoc( * * @param array $one * @param array $two - * @param callable(TV, TV): int<-1, 1> $three + * @param callable(TV, TV): int $three * @return array */ function array_uintersect_assoc( @@ -183,8 +183,8 @@ function array_uintersect_assoc( * * @param array $one * @param array $two - * @param callable(TV, TV): int<-1, 1> $three - * @param callable(TK, TK): int<-1, 1> $four + * @param callable(TV, TV): int $three + * @param callable(TK, TK): int $four * @return array */ function array_uintersect_uassoc( @@ -200,7 +200,7 @@ function array_uintersect_uassoc( * * @param array $one * @param array $two - * @param callable(TV, TV): int<-1, 1> $three + * @param callable(TV, TV): int $three * @return array */ function array_uintersect( diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 198ee2d2c7..026997b36e 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1721,11 +1721,11 @@ public function testArrayDiffUassoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_diff_uassoc.php'], [ [ - 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_comp_func of function array_diff_uassoc expects callable(0|1|2|3, 0|1|2|3): int, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1736,11 +1736,11 @@ public function testArrayDiffUkey(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_diff_ukey.php'], [ [ - 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_comp_func of function array_diff_ukey expects callable(0|1|2|3, 0|1|2|3): int, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1751,11 +1751,11 @@ public function testArrayIntersectUassoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_intersect_uassoc.php'], [ [ - 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_compare_func of function array_intersect_uassoc expects callable(0|1|2|3, 0|1|2|3): int, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1766,11 +1766,11 @@ public function testArrayIntersectUkey(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_intersect_ukey.php'], [ [ - 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_compare_func of function array_intersect_ukey expects callable(0|1|2|3, 0|1|2|3): int, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1781,11 +1781,11 @@ public function testArrayUdiffAssoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_udiff_assoc.php'], [ [ - 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(1|2, 1|2): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(1|2, 1|2): int, Closure(string, string): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(1|2|3|4|5, 1|2|3|4|5): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $key_comp_func of function array_udiff_assoc expects callable(1|2|3|4|5, 1|2|3|4|5): int, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1796,19 +1796,19 @@ public function testArrayUdiffUasssoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_udiff_uassoc.php'], [ [ - 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 28, ], [ - 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 31, ], [ - 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(1|2|3|4|5, 1|2|3|4|5): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_comp_func of function array_udiff_uassoc expects callable(1|2|3|4|5, 1|2|3|4|5): int, Closure(string, string): int<-1, 1> given.', 39, ], [ - 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #4 $key_comp_func of function array_udiff_uassoc expects callable(0|1|2|3, 0|1|2|3): int, Closure(string, string): int<-1, 1> given.', 42, ], ]); @@ -1819,11 +1819,11 @@ public function testArrayUintersectAssoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_uintersect_assoc.php'], [ [ - 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(1|2|3|4, 1|2|3|4): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect_assoc expects callable(1|2|3|4, 1|2|3|4): int, Closure(string, string): int<-1, 1> given.', 30, ], ]); @@ -1834,19 +1834,19 @@ public function testArrayUintersectUassoc(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_uintersect_uassoc.php'], [ [ - 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 28, ], [ - 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 31, ], [ - 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(1|2|3|4|5, 1|2|3|4|5): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect_uassoc expects callable(1|2|3|4|5, 1|2|3|4|5): int, Closure(string, string): int<-1, 1> given.', 39, ], [ - 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(0|1|2|3, 0|1|2|3): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #4 $key_compare_func of function array_uintersect_uassoc expects callable(0|1|2|3, 0|1|2|3): int, Closure(string, string): int<-1, 1> given.', 42, ], ]); @@ -1857,11 +1857,11 @@ public function testArrayUintersect(): void $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/array_uintersect.php'], [ [ - 'Parameter #3 $data_compare_func of function array_uintersect expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int<-1, 1>, Closure(int, int): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect expects callable(\'a\'|\'b\'|\'c\'|\'d\', \'a\'|\'b\'|\'c\'|\'d\'): int, Closure(int, int): int<-1, 1> given.', 22, ], [ - 'Parameter #3 $data_compare_func of function array_uintersect expects callable(1|2|3|4, 1|2|3|4): int<-1, 1>, Closure(string, string): int<-1, 1> given.', + 'Parameter #3 $data_compare_func of function array_uintersect expects callable(1|2|3|4, 1|2|3|4): int, Closure(string, string): int<-1, 1> given.', 30, ], ]); From f6b15ca756c01867467034b33b66d4a9e5a5c67f Mon Sep 17 00:00:00 2001 From: schlndh Date: Fri, 13 Sep 2024 12:38:51 +0200 Subject: [PATCH 5/6] fix array_udiff stub --- stubs/arrayFunctions.stub | 12 +++++++----- .../Functions/CallToFunctionParametersRuleTest.php | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/stubs/arrayFunctions.stub b/stubs/arrayFunctions.stub index b17bf519eb..a638e47380 100644 --- a/stubs/arrayFunctions.stub +++ b/stubs/arrayFunctions.stub @@ -50,17 +50,19 @@ function uksort(array &$array, callable $callback): bool } /** - * @template T of mixed + * @template TV of mixed + * @template TK of mixed * - * @param array $one - * @param array $two - * @param callable(T, T): int $three + * @param array $one + * @param array $two + * @param callable(TV, TV): int $three + * @return array */ function array_udiff( array $one, array $two, callable $three -): int {} +): array {} /** * @param array $value diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 026997b36e..6ba69f9432 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -640,11 +640,11 @@ public function testArrayUdiffCallback(): void 14, ], [ - 'Parameter #1 $arr1 of function array_udiff expects array, null given.', + 'Parameter #1 $arr1 of function array_udiff expects array<(int&TK)|(string&TK), string>, null given.', 20, ], [ - 'Parameter #2 $arr2 of function array_udiff expects array, null given.', + 'Parameter #2 $arr2 of function array_udiff expects array<(int&TK)|(string&TK), string>, null given.', 21, ], [ From cbc7c87d31c9f4355c200dffb6eb2dc083873f61 Mon Sep 17 00:00:00 2001 From: schlndh Date: Fri, 13 Sep 2024 12:57:15 +0200 Subject: [PATCH 6/6] add NodeScopeResolver test to make sure correct types are specified inside closures --- .../nsrt/array_diff_intersect_callbacks.php | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/array_diff_intersect_callbacks.php diff --git a/tests/PHPStan/Analyser/nsrt/array_diff_intersect_callbacks.php b/tests/PHPStan/Analyser/nsrt/array_diff_intersect_callbacks.php new file mode 100644 index 0000000000..5dc721664e --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/array_diff_intersect_callbacks.php @@ -0,0 +1,127 @@ +