diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index 0b3478ea9..85000bec7 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -8,6 +8,7 @@ use Psalm\Type\Atomic; 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; @@ -191,7 +192,12 @@ final class AtomicTypeComparator } 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) ) { diff --git a/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php b/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php index 4c7c3b8c7..393c83691 100644 --- a/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php @@ -19,6 +19,9 @@ use Psalm\Type\Atomic; 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; @@ -41,7 +44,7 @@ use function substr; 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( @@ -50,6 +53,26 @@ final class CallableTypeComparator 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; diff --git a/tests/CallableTest.php b/tests/CallableTest.php index e10784b0b..eff0e838a 100644 --- a/tests/CallableTest.php +++ b/tests/CallableTest.php @@ -1920,6 +1920,21 @@ class CallableTest extends TestCase 'ignored_issues' => [], 'php_version' => '8.0', ], + 'callableArrayPassedAsCallable' => [ + 'code' => <<<'PHP' + [ + 'callable', + "callable-array{0: class-string, 1: 'from'}", + ], + 'callableAcceptsCallableObject' => [ + 'callable', + "callable-object", + ], + 'callableAcceptsCallableString' => [ + 'callable', + 'callable-string', + ], + 'callableAcceptsCallableKeyedList' => [ + 'callable', + "callable-list{class-string, 'from'}", + ], ]; }