mirror of
https://github.com/danog/psalm.git
synced 2024-12-02 09:37:59 +01:00
Merge pull request #10805 from weirdan/10461-allow-more-callable-types-as-subtypes-of-callable
This commit is contained in:
commit
f61c7e108f
@ -8,6 +8,7 @@ use Psalm\Type\Atomic;
|
|||||||
use Psalm\Type\Atomic\Scalar;
|
use Psalm\Type\Atomic\Scalar;
|
||||||
use Psalm\Type\Atomic\TArray;
|
use Psalm\Type\Atomic\TArray;
|
||||||
use Psalm\Type\Atomic\TCallable;
|
use Psalm\Type\Atomic\TCallable;
|
||||||
|
use Psalm\Type\Atomic\TCallableInterface;
|
||||||
use Psalm\Type\Atomic\TCallableKeyedArray;
|
use Psalm\Type\Atomic\TCallableKeyedArray;
|
||||||
use Psalm\Type\Atomic\TCallableObject;
|
use Psalm\Type\Atomic\TCallableObject;
|
||||||
use Psalm\Type\Atomic\TCallableString;
|
use Psalm\Type\Atomic\TCallableString;
|
||||||
@ -191,7 +192,8 @@ final class AtomicTypeComparator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (($container_type_part instanceof TCallable
|
if (($container_type_part instanceof TCallable
|
||||||
&& $input_type_part instanceof TCallable)
|
&& $input_type_part instanceof TCallableInterface
|
||||||
|
)
|
||||||
|| ($container_type_part instanceof TClosure
|
|| ($container_type_part instanceof TClosure
|
||||||
&& $input_type_part instanceof TClosure)
|
&& $input_type_part instanceof TClosure)
|
||||||
) {
|
) {
|
||||||
|
@ -19,6 +19,7 @@ use Psalm\Type\Atomic;
|
|||||||
use Psalm\Type\Atomic\TArray;
|
use Psalm\Type\Atomic\TArray;
|
||||||
use Psalm\Type\Atomic\TCallable;
|
use Psalm\Type\Atomic\TCallable;
|
||||||
use Psalm\Type\Atomic\TCallableArray;
|
use Psalm\Type\Atomic\TCallableArray;
|
||||||
|
use Psalm\Type\Atomic\TCallableInterface;
|
||||||
use Psalm\Type\Atomic\TClassString;
|
use Psalm\Type\Atomic\TClassString;
|
||||||
use Psalm\Type\Atomic\TClosure;
|
use Psalm\Type\Atomic\TClosure;
|
||||||
use Psalm\Type\Atomic\TKeyedArray;
|
use Psalm\Type\Atomic\TKeyedArray;
|
||||||
@ -41,15 +42,31 @@ use function substr;
|
|||||||
final class CallableTypeComparator
|
final class CallableTypeComparator
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param TCallable|TClosure $input_type_part
|
* @param TClosure|TCallableInterface $input_type_part
|
||||||
* @param TCallable|TClosure $container_type_part
|
* @param TCallable|TClosure $container_type_part
|
||||||
*/
|
*/
|
||||||
public static function isContainedBy(
|
public static function isContainedBy(
|
||||||
Codebase $codebase,
|
Codebase $codebase,
|
||||||
Atomic $input_type_part,
|
$input_type_part,
|
||||||
Atomic $container_type_part,
|
Atomic $container_type_part,
|
||||||
?TypeComparisonResult $atomic_comparison_result
|
?TypeComparisonResult $atomic_comparison_result
|
||||||
): bool {
|
): bool {
|
||||||
|
if ($container_type_part instanceof TClosure) {
|
||||||
|
if ($input_type_part instanceof TCallableInterface
|
||||||
|
&& !$input_type_part instanceof TCallable // it has stricter checks below
|
||||||
|
) {
|
||||||
|
if ($atomic_comparison_result) {
|
||||||
|
$atomic_comparison_result->type_coerced = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($input_type_part instanceof TCallableInterface
|
||||||
|
&& !$input_type_part instanceof TCallable // it has stricter checks below
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($container_type_part->is_pure && !$input_type_part->is_pure) {
|
if ($container_type_part->is_pure && !$input_type_part->is_pure) {
|
||||||
if ($atomic_comparison_result) {
|
if ($atomic_comparison_result) {
|
||||||
$atomic_comparison_result->type_coerced = $input_type_part->is_pure === null;
|
$atomic_comparison_result->type_coerced = $input_type_part->is_pure === null;
|
||||||
|
@ -14,7 +14,7 @@ use Psalm\Type\Union;
|
|||||||
*
|
*
|
||||||
* @psalm-immutable
|
* @psalm-immutable
|
||||||
*/
|
*/
|
||||||
final class TCallable extends Atomic
|
final class TCallable extends Atomic implements TCallableInterface
|
||||||
{
|
{
|
||||||
use CallableTrait;
|
use CallableTrait;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ namespace Psalm\Type\Atomic;
|
|||||||
*
|
*
|
||||||
* @psalm-immutable
|
* @psalm-immutable
|
||||||
*/
|
*/
|
||||||
final class TCallableArray extends TNonEmptyArray
|
final class TCallableArray extends TNonEmptyArray implements TCallableInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
|
7
src/Psalm/Type/Atomic/TCallableInterface.php
Normal file
7
src/Psalm/Type/Atomic/TCallableInterface.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psalm\Type\Atomic;
|
||||||
|
|
||||||
|
interface TCallableInterface
|
||||||
|
{
|
||||||
|
}
|
@ -7,7 +7,7 @@ namespace Psalm\Type\Atomic;
|
|||||||
*
|
*
|
||||||
* @psalm-immutable
|
* @psalm-immutable
|
||||||
*/
|
*/
|
||||||
final class TCallableKeyedArray extends TKeyedArray
|
final class TCallableKeyedArray extends TKeyedArray implements TCallableInterface
|
||||||
{
|
{
|
||||||
protected const NAME_ARRAY = 'callable-array';
|
protected const NAME_ARRAY = 'callable-array';
|
||||||
protected const NAME_LIST = 'callable-list';
|
protected const NAME_LIST = 'callable-list';
|
||||||
|
@ -12,7 +12,7 @@ use function array_fill;
|
|||||||
* @deprecated Will be removed in Psalm v6, please use TCallableKeyedArrays with is_list=true instead.
|
* @deprecated Will be removed in Psalm v6, please use TCallableKeyedArrays with is_list=true instead.
|
||||||
* @psalm-immutable
|
* @psalm-immutable
|
||||||
*/
|
*/
|
||||||
final class TCallableList extends TNonEmptyList
|
final class TCallableList extends TNonEmptyList implements TCallableInterface
|
||||||
{
|
{
|
||||||
public const KEY = 'callable-list';
|
public const KEY = 'callable-list';
|
||||||
public function getKeyedArray(): TKeyedArray
|
public function getKeyedArray(): TKeyedArray
|
||||||
|
@ -7,7 +7,7 @@ namespace Psalm\Type\Atomic;
|
|||||||
*
|
*
|
||||||
* @psalm-immutable
|
* @psalm-immutable
|
||||||
*/
|
*/
|
||||||
final class TCallableObject extends TObject
|
final class TCallableObject extends TObject implements TCallableInterface
|
||||||
{
|
{
|
||||||
use HasIntersectionTrait;
|
use HasIntersectionTrait;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ namespace Psalm\Type\Atomic;
|
|||||||
*
|
*
|
||||||
* @psalm-immutable
|
* @psalm-immutable
|
||||||
*/
|
*/
|
||||||
final class TCallableString extends TNonFalsyString
|
final class TCallableString extends TNonFalsyString implements TCallableInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
public function getKey(bool $include_extra = true): string
|
public function getKey(bool $include_extra = true): string
|
||||||
|
@ -1920,6 +1920,21 @@ class CallableTest extends TestCase
|
|||||||
'ignored_issues' => [],
|
'ignored_issues' => [],
|
||||||
'php_version' => '8.0',
|
'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,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +163,22 @@ class TypeComparatorTest extends TestCase
|
|||||||
'(callable(int,string[]): void)|(callable(int): void)',
|
'(callable(int,string[]): void)|(callable(int): void)',
|
||||||
'(callable(int): void)|(callable(int,string[]): 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'}",
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user