Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion src/Type/Php/ClosureBindDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\ClosureType;
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;

#[AutowiredService]
final class ClosureBindDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
Expand All @@ -32,7 +34,7 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
return null;
}

return $closureType;
return TypeCombinator::union($closureType, new NullType());
}

}
4 changes: 3 additions & 1 deletion src/Type/Php/ClosureBindToDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\ClosureType;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;

#[AutowiredService]
final class ClosureBindToDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
Expand All @@ -32,7 +34,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
return null;
}

return $closureType;
return TypeCombinator::union($closureType, new NullType());
}

}
10 changes: 7 additions & 3 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -473,13 +473,17 @@ public function testBug4734(): void
{
// false positive
$errors = $this->runAnalyse(__DIR__ . '/data/bug-4734.php');
$this->assertCount(5, $errors); // could be 3
$this->assertCount(9, $errors); // could be 3

$this->assertSame('Static property Bug4734\Foo::$httpMethodParameterOverride (bool) is never assigned false so the property type can be changed to true.', $errors[0]->getMessage()); // should not error
$this->assertSame('Property Bug4734\Foo::$httpMethodParameterOverride2 (bool) is never assigned false so the property type can be changed to true.', $errors[1]->getMessage()); // should not error
$this->assertSame('Unsafe access to private property Bug4734\Foo::$httpMethodParameterOverride through static::.', $errors[2]->getMessage());
$this->assertSame('Access to an undefined static property static(Bug4734\Foo)::$httpMethodParameterOverride3.', $errors[3]->getMessage());
$this->assertSame('Access to an undefined property Bug4734\Foo::$httpMethodParameterOverride4.', $errors[4]->getMessage());
$this->assertSame('Trying to invoke (Closure(): void)|null but it might not be a callable.', $errors[3]->getMessage());
$this->assertSame('Trying to invoke (Closure(): void)|null but it might not be a callable.', $errors[4]->getMessage());
$this->assertSame('Access to an undefined static property static(Bug4734\Foo)::$httpMethodParameterOverride3.', $errors[5]->getMessage());
$this->assertSame('Trying to invoke (Closure(): void)|null but it might not be a callable.', $errors[6]->getMessage());
$this->assertSame('Access to an undefined property Bug4734\Foo::$httpMethodParameterOverride4.', $errors[7]->getMessage());
$this->assertSame('Trying to invoke (Closure(): void)|null but it might not be a callable.', $errors[8]->getMessage());
}

#[RequiresPhp('>= 8.1.0')]
Expand Down
25 changes: 25 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-5009.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types = 1);

namespace Bug5009;

use Closure;
use function PHPStan\Testing\assertType;

$foo = function (): void {};
$bar = $foo->bindTo(null);
assertType('(Closure(): void)|null', $bar);

$baz = Closure::bind($foo, null);
assertType('(Closure(): void)|null', $baz);

$newThis = new \stdClass();
$bound = $foo->bindTo($newThis);
assertType('(Closure(): void)|null', $bound);

$staticBound = Closure::bind($foo, $newThis);
assertType('(Closure(): void)|null', $staticBound);

$bound2 = $foo->bindTo($newThis, 'stdClass');
assertType('(Closure(): void)|null', $bound2);
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

$newThis = new class {};
$boundClosure = $closure->bindTo($newThis);
assertType('Closure(object): true', $boundClosure);
assertType('(Closure(object): true)|null', $boundClosure);

$staticallyBoundClosure = \Closure::bind($closure, $newThis);
assertType('Closure(object): true', $staticallyBoundClosure);
assertType('(Closure(object): true)|null', $staticallyBoundClosure);

$returnType = $closure->call($newThis, new class {});
assertType('true', $returnType);

$staticallyBoundClosureCaseInsensitive = \closure::bind($closure, $newThis);
assertType('Closure(object): true', $staticallyBoundClosureCaseInsensitive);
assertType('(Closure(object): true)|null', $staticallyBoundClosureCaseInsensitive);
Loading