diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 2b3125ed6c7..2f8cbc9f8db 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -105,6 +105,7 @@ use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ErrorType; use PHPStan\Type\FileTypeMapper; +use PHPStan\Type\Generic\GenericClassStringType; use PHPStan\Type\Generic\TemplateTypeHelper; use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\IntegerType; @@ -2122,6 +2123,12 @@ static function () use ($scope, $expr): MutatingScope { } elseif ($argValueType instanceof ConstantStringType) { $scopeClass = $argValueType->getValue(); $thisType = new ObjectType($scopeClass); + } elseif ( + $argValueType instanceof GenericClassStringType + && $argValueType->getGenericType() instanceof ConstantStringType + ) { + $scopeClass = $argValueType->getGenericType()->getValue(); + $thisType = new ObjectType($scopeClass); } } $closureBindScope = $scope->enterClosureBind($thisType, $scopeClass); diff --git a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php index 6fea88f4600..ccc70a0afc2 100644 --- a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php @@ -863,7 +863,11 @@ public function testClosureBind(): void ], [ 'Call to an undefined method CallClosureBind\Foo::nonexistentMethod().', - 39, + 38, + ], + [ + 'Call to an undefined method CallClosureBind\Foo::nonexistentMethod().', + 44, ], ]); } diff --git a/tests/PHPStan/Rules/Methods/data/closure-bind.php b/tests/PHPStan/Rules/Methods/data/closure-bind.php index e4dec44d161..f6aa9e05b73 100644 --- a/tests/PHPStan/Rules/Methods/data/closure-bind.php +++ b/tests/PHPStan/Rules/Methods/data/closure-bind.php @@ -33,6 +33,11 @@ public function fooMethod(): Foo $foo->nonexistentMethod(); }, null, new Foo()); + \Closure::bind(function (Foo $foo) { + $foo->privateMethod(); + $foo->nonexistentMethod(); + }, null, get_class(new Foo())); + \Closure::bind(function () { // $this is Foo $this->privateMethod();