Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Type/CallableTypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public static function isParametersAcceptorSuperTypeOf(
$isSuperType = $theirParameter->getType()->accepts($ourParameterType, $strictTypes);
$isSuperType = new IsSuperTypeOfResult($isSuperType->result, $isSuperType->reasons);
} else {
$isSuperType = $theirParameter->getType()->isSuperTypeOf($ourParameterType);
$isSuperType = $ourParameterType->isSuperTypeOf($theirParameter->getType());
}

if ($isSuperType->maybe()) {
Expand Down
58 changes: 58 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-11285.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php // lint >= 8.0

declare(strict_types = 1);

namespace Bug11285;

use function PHPStan\Testing\assertType;

/**
* @template T
* @template U
*/
class Templated
{

/**
* @param callable(T|U): void $callback
*/
public function compare(callable $callback): void {

}
}

class ClassA {}
class ClassB {}


class Test
{
/**
* @param Templated<ClassA, ClassB>|Templated<ClassA, ClassA> $t
*/
public function broken(
Templated $t,
): void
{
$t->compare(
static function (ClassA|ClassB $crate): void {
assertType('Bug11285\\ClassA|Bug11285\\ClassB', $crate);
}
);
}

/**
* @param Templated<ClassA, ClassB> $t
*/
public function working(
Templated $t,
): void
{
$t->compare(
static function (ClassA|ClassB $crate): void {
assertType('Bug11285\\ClassA|Bug11285\\ClassB', $crate);
}
);
}

}
10 changes: 5 additions & 5 deletions tests/PHPStan/Type/CallableTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ public static function dataIsSuperTypeOf(): array
[
new CallableType([new NativeParameterReflection('foo', false, new MixedType(), PassedByReference::createNo(), false, null)], new MixedType(), false),
new CallableType([new NativeParameterReflection('foo', false, new IntegerType(), PassedByReference::createNo(), false, null)], new MixedType(), false),
TrinaryLogic::createMaybe(),
TrinaryLogic::createYes(),
],
[
new CallableType([new NativeParameterReflection('foo', false, new IntegerType(), PassedByReference::createNo(), false, null)], new MixedType(), false),
new CallableType([new NativeParameterReflection('foo', false, new MixedType(), PassedByReference::createNo(), false, null)], new MixedType(), false),
TrinaryLogic::createYes(),
TrinaryLogic::createMaybe(),
],
[
new CallableType([
Expand Down Expand Up @@ -432,8 +432,8 @@ public function testIsSuperTypeOfKeepsLazyReasonsOfMaybeParameter(): void
$accepting = new CallableType([new NativeParameterReflection('x', false, $acceptingParameterType, PassedByReference::createNo(), false, null)], new VoidType());
$passed = new CallableType([new NativeParameterReflection('x', false, $passedParameterType, PassedByReference::createNo(), false, null)], new VoidType());

// the passed parameter's isSuperTypeOf() is maybe, with a lazy reason from its array{b: int} member
$parameterResult = $passedParameterType->isSuperTypeOf($acceptingParameterType);
// the accepting parameter's isSuperTypeOf() is maybe, with a lazy reason from the passed array{b: int} member
$parameterResult = $acceptingParameterType->isSuperTypeOf($passedParameterType);
$this->assertTrue($parameterResult->maybe());
$this->assertNotSame([], $parameterResult->lazyReasons);

Expand All @@ -442,7 +442,7 @@ public function testIsSuperTypeOfKeepsLazyReasonsOfMaybeParameter(): void
$reasons = $result->getReasons();
$this->assertCount(2, $reasons);
$this->assertSame('Type array{a: int}|array{b: int} of parameter #1 $x of passed callable needs to be same or wider than parameter type array{a: int, b?: int} of accepting callable.', $reasons[0]);
$this->assertStringStartsWith('Sealed array shapes array{b: int} and array{a: int, b?: int} cannot be intersected.', $reasons[1]);
$this->assertStringStartsWith('Sealed array shapes array{a: int, b?: int} and array{b: int} cannot be intersected.', $reasons[1]);
}

}
31 changes: 31 additions & 0 deletions tests/PHPStan/Type/TypeCombinatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use PHPStan\Generics\FunctionsAssertType\C;
use PHPStan\PhpDoc\TypeStringResolver;
use PHPStan\Reflection\Callables\SimpleImpurePoint;
use PHPStan\Reflection\Php\DummyParameter;
use PHPStan\Testing\PHPStanTestCase;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Accessory\AccessoryArrayListType;
Expand Down Expand Up @@ -3244,6 +3245,36 @@ public static function dataUnion(): iterable
ConstantArrayType::class,
'array{}',
];
yield [
[
new CallableType([
new DummyParameter('callback', new ObjectType('stdClass'), false, null, false, null),
], new VoidType(), false),
new CallableType([
new DummyParameter('callback', new UnionType([
new ObjectType('stdClass'),
new ObjectType('Exception'),
]), false, null, false, null),
], new VoidType(), false),
],
CallableType::class,
'callable(Exception|stdClass): void',
];
yield [
[
new CallableType([
new DummyParameter('callback', new ObjectType('stdClass'), false, null, false, null),
], new VoidType(), false),
new CallableType([
new DummyParameter('callback', new UnionType([
new IntegerType(),
new ObjectType('Exception'),
]), false, null, false, null),
], new VoidType(), false),
],
UnionType::class,
'(callable(Exception|int): void)|(callable(stdClass): void)',
];
}

/**
Expand Down
2 changes: 1 addition & 1 deletion turbo-ext/src/CallableTypeHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class CallableTypeHelper
if (UNEXPECTED(accepts.isUndef())) return zv::Val();
isSuperType = resultFrom(accepts.raw());
} else {
isSuperType = callObject(theirParameterType.raw(), PT_LC("issupertypeof"), 1, ourParameterType.raw());
isSuperType = callObject(ourParameterType.raw(), PT_LC("issupertypeof"), 1, theirParameterType.raw());
}
if (UNEXPECTED(isSuperType.isUndef())) return zv::Val();

Expand Down
Loading