Skip to content

Commit

Permalink
Allow more callable types as subtypes of callable
Browse files Browse the repository at this point in the history
Fixes #10461
  • Loading branch information
weirdan committed Mar 10, 2024
1 parent c511185 commit 20c7889
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 2 deletions.
8 changes: 7 additions & 1 deletion src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Psalm\Type\Atomic\Scalar;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TCallable;
use Psalm\Type\Atomic\TCallableArray;
use Psalm\Type\Atomic\TCallableKeyedArray;
use Psalm\Type\Atomic\TCallableObject;
use Psalm\Type\Atomic\TCallableString;
Expand Down Expand Up @@ -191,7 +192,12 @@ public static function isContainedBy(
}

if (($container_type_part instanceof TCallable
&& $input_type_part instanceof TCallable)
&& ($input_type_part instanceof TCallable
|| $input_type_part instanceof TCallableArray
|| $input_type_part instanceof TCallableObject
|| $input_type_part instanceof TCallableString
|| $input_type_part instanceof TCallableKeyedArray
))
|| ($container_type_part instanceof TClosure
&& $input_type_part instanceof TClosure)
) {
Expand Down
25 changes: 24 additions & 1 deletion src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TCallable;
use Psalm\Type\Atomic\TCallableArray;
use Psalm\Type\Atomic\TCallableKeyedArray;
use Psalm\Type\Atomic\TCallableObject;
use Psalm\Type\Atomic\TCallableString;
use Psalm\Type\Atomic\TClassString;
use Psalm\Type\Atomic\TClosure;
use Psalm\Type\Atomic\TKeyedArray;
Expand All @@ -41,7 +44,7 @@
final class CallableTypeComparator
{
/**
* @param TCallable|TClosure $input_type_part
* @param TCallable|TClosure|TCallableArray|TCallableString|TCallableKeyedArray|TCallableObject $input_type_part
* @param TCallable|TClosure $container_type_part
*/
public static function isContainedBy(
Expand All @@ -50,6 +53,26 @@ public static function isContainedBy(
Atomic $container_type_part,
?TypeComparisonResult $atomic_comparison_result
): bool {
if ($container_type_part instanceof TClosure) {
if ($input_type_part instanceof TCallableArray
|| $input_type_part instanceof TCallableString
|| $input_type_part instanceof TCallableKeyedArray
|| $input_type_part instanceof TCallableObject
) {
if ($atomic_comparison_result) {
$atomic_comparison_result->type_coerced = true;
}
return false;
}
}
if ($input_type_part instanceof TCallableArray
|| $input_type_part instanceof TCallableString
|| $input_type_part instanceof TCallableKeyedArray
|| $input_type_part instanceof TCallableObject
) {
return true;
}

if ($container_type_part->is_pure && !$input_type_part->is_pure) {
if ($atomic_comparison_result) {
$atomic_comparison_result->type_coerced = $input_type_part->is_pure === null;
Expand Down
15 changes: 15 additions & 0 deletions tests/CallableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1920,6 +1920,21 @@ function f(callable $c): void {
'ignored_issues' => [],
'php_version' => '8.0',
],
'callableArrayPassedAsCallable' => [
'code' => <<<'PHP'
<?php
function f(callable $c): void {
$c();
}
/** @var object $o */;
$ca = [$o::class, 'createFromFormat'];
if (!is_callable($ca)) {
exit;
}
f($ca);
PHP,
],
];
}

Expand Down
16 changes: 16 additions & 0 deletions tests/TypeComparatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,22 @@ public function getSuccessfulComparisons(): array
'(callable(int,string[]): void)|(callable(int): void)',
'(callable(int): void)|(callable(int,string[]): void)',
],
'callableAcceptsCallableArray' => [
'callable',
"callable-array{0: class-string, 1: 'from'}",
],
'callableAcceptsCallableObject' => [
'callable',
"callable-object",
],
'callableAcceptsCallableString' => [
'callable',
'callable-string',
],
'callableAcceptsCallableKeyedList' => [
'callable',
"callable-list{class-string, 'from'}",
],
];
}

Expand Down

0 comments on commit 20c7889

Please sign in to comment.