From 542cb0a75eacce7a07d8d75240d843c7f8a85527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Mon, 22 Apr 2024 12:34:16 +0200 Subject: [PATCH] Narrow Closure::bind $newScope param --- resources/functionMap_bleedingEdge.php | 2 ++ .../Php8SignatureMapProviderTest.php | 4 ++- .../Rules/Methods/CallMethodsRuleTest.php | 4 +++ .../Methods/CallStaticMethodsRuleTest.php | 11 +++++++ .../Rules/Methods/data/closure-bind.php | 29 +++++++++++++++++++ 5 files changed, 49 insertions(+), 1 deletion(-) diff --git a/resources/functionMap_bleedingEdge.php b/resources/functionMap_bleedingEdge.php index c13badb685..7c54feb8a6 100644 --- a/resources/functionMap_bleedingEdge.php +++ b/resources/functionMap_bleedingEdge.php @@ -2,6 +2,8 @@ return [ 'new' => [ + 'Closure::bind' => ['Closure', 'old'=>'Closure', 'to'=>'?object', 'scope='=>'object|class-string|\'static\'|null'], + 'Closure::bindTo' => ['Closure', 'new'=>'?object', 'newscope='=>'object|class-string|\'static\'|null'], 'error_log' => ['bool', 'message'=>'string', 'message_type='=>'0|1|2|3|4', 'destination='=>'string', 'extra_headers='=>'string'], 'SplFileObject::flock' => ['bool', 'operation'=>'int-mask', '&w_wouldblock='=>'0|1'], 'Imagick::adaptiveBlurImage' => ['bool', 'radius'=>'float', 'sigma'=>'float', 'channel='=>'Imagick::CHANNEL_*'], diff --git a/tests/PHPStan/Reflection/SignatureMap/Php8SignatureMapProviderTest.php b/tests/PHPStan/Reflection/SignatureMap/Php8SignatureMapProviderTest.php index ad5aa4077f..88a1b9d9c7 100644 --- a/tests/PHPStan/Reflection/SignatureMap/Php8SignatureMapProviderTest.php +++ b/tests/PHPStan/Reflection/SignatureMap/Php8SignatureMapProviderTest.php @@ -14,6 +14,7 @@ use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; use PHPStan\Type\CallableType; +use PHPStan\Type\ClassStringType; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\Constant\ConstantStringType; @@ -189,7 +190,8 @@ public function dataMethods(): array 'optional' => true, 'type' => new UnionType([ new ObjectWithoutClassType(), - new StringType(), + new ClassStringType(), + new ConstantStringType('static'), new NullType(), ]), 'nativeType' => new UnionType([ diff --git a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php index 961ef99ba9..2219328e27 100644 --- a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php @@ -968,6 +968,10 @@ public function testClosureBind(): void 'Call to an undefined method CallClosureBind\Foo::nonexistentMethod().', 44, ], + [ + 'Parameter #2 $newScope of method Closure::bindTo() expects \'static\'|class-string|object|null, \'CallClosureBind\\\Bar3\' given.', + 74, + ], ]); } diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index b117ae5da1..f9886774fc 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -817,4 +817,15 @@ public function testConditionalParam(): void ]); } + public function testClosureBind(): void + { + $this->checkThisOnly = false; + $this->analyse([__DIR__ . '/data/closure-bind.php'], [ + [ + 'Parameter #3 $newScope of static method Closure::bind() expects \'static\'|class-string|object|null, \'CallClosureBind\\\Bar3\' given.', + 68, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/closure-bind.php b/tests/PHPStan/Rules/Methods/data/closure-bind.php index f6aa9e05b7..57b9ccf5ff 100644 --- a/tests/PHPStan/Rules/Methods/data/closure-bind.php +++ b/tests/PHPStan/Rules/Methods/data/closure-bind.php @@ -49,4 +49,33 @@ public function fooMethod(): Foo })->call(new Foo()); } + public function x(): bool + { + return 1.0; + } + + public function testClassString(): bool + { + $fx = function () { + return $this->x(); + }; + + $res = 0.0; + $res += \Closure::bind($fx, $this)(); + $res += \Closure::bind($fx, $this, 'static')(); + $res += \Closure::bind($fx, $this, Foo2::class)(); + $res += \Closure::bind($fx, $this, 'CallClosureBind\Bar2')(); + $res += \Closure::bind($fx, $this, 'CallClosureBind\Bar3')(); + + $res += $fx->bindTo($this)(); + $res += $fx->bindTo($this, 'static')(); + $res += $fx->bindTo($this, Foo2::class)(); + $res += $fx->bindTo($this, 'CallClosureBind\Bar2')(); + $res += $fx->bindTo($this, 'CallClosureBind\Bar3')(); + + return $res; + } + } + +class Bar2 extends Bar {}