Skip to content

Commit

Permalink
Fix late static binding calls for first class callable
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Sep 19, 2024
1 parent e629cee commit 9263039
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 31 deletions.
50 changes: 23 additions & 27 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,7 @@ private function resolveType(string $exprString, Expr $node): Type
return new ObjectType(Closure::class);
}

$classType = $this->resolveTypeByName($node->class);
$classType = $this->resolveTypeByNameWithLateStaticBinding($node->class, $node->name);
$methodName = $node->name->toString();
if (!$classType->hasMethod($methodName)->yes()) {
return new ObjectType(Closure::class);
Expand Down Expand Up @@ -2082,19 +2082,7 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
if ($this->nativeTypesPromoted) {
$typeCallback = function () use ($node): Type {
if ($node->class instanceof Name) {
$staticMethodCalledOnType = $this->resolveTypeByName($node->class);
if (
$staticMethodCalledOnType instanceof StaticType
&& !in_array($node->class->toLowerString(), ['self', 'static', 'parent'], true)
) {
$methodReflectionCandidate = $this->getMethodReflection(
$staticMethodCalledOnType,
$node->name->name,
);
if ($methodReflectionCandidate !== null && $methodReflectionCandidate->isStatic()) {
$staticMethodCalledOnType = $staticMethodCalledOnType->getStaticObjectType();
}
}
$staticMethodCalledOnType = $this->resolveTypeByNameWithLateStaticBinding($node->class, $node->name);
} else {
$staticMethodCalledOnType = $this->getNativeType($node->class);
}
Expand All @@ -2119,19 +2107,7 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu

$typeCallback = function () use ($node): Type {
if ($node->class instanceof Name) {
$staticMethodCalledOnType = $this->resolveTypeByName($node->class);
if (
$staticMethodCalledOnType instanceof StaticType
&& !in_array($node->class->toLowerString(), ['self', 'static', 'parent'], true)
) {
$methodReflectionCandidate = $this->getMethodReflection(
$staticMethodCalledOnType,
$node->name->name,
);
if ($methodReflectionCandidate !== null && $methodReflectionCandidate->isStatic()) {
$staticMethodCalledOnType = $staticMethodCalledOnType->getStaticObjectType();
}
}
$staticMethodCalledOnType = $this->resolveTypeByNameWithLateStaticBinding($node->class, $node->name);
} else {
$staticMethodCalledOnType = TypeCombinator::removeNull($this->getType($node->class))->getObjectTypeOrClassStringObjectType();
}
Expand Down Expand Up @@ -2780,6 +2756,26 @@ public function resolveTypeByName(Name $name): TypeWithClassName
return new ObjectType($originalClass);
}

private function resolveTypeByNameWithLateStaticBinding(Name $class, Node\Identifier $name): TypeWithClassName
{
$classType = $this->resolveTypeByName($class);

if (
$classType instanceof StaticType
&& !in_array($class->toLowerString(), ['self', 'static', 'parent'], true)
) {
$methodReflectionCandidate = $this->getMethodReflection(
$classType,
$name->name,
);
if ($methodReflectionCandidate !== null && $methodReflectionCandidate->isStatic()) {
$classType = $classType->getStaticObjectType();
}
}

return $classType;
}

/**
* @api
* @param mixed $value
Expand Down
17 changes: 13 additions & 4 deletions tests/PHPStan/Analyser/nsrt/static-late-binding.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function foo(): void
assertType('int', parent::retStaticConst());
assertType('2', $this->retStaticConst());
assertType('bool', X::retStaticConst());
assertType('*ERROR*', $clUnioned->retStaticConst()); // should be bool|int
assertType('*ERROR*', $clUnioned->retStaticConst()); // should be bool|int https://github.com/phpstan/phpstan/issues/11687

assertType('int', A::retStaticConst(...)());
assertType('2', B::retStaticConst(...)());
Expand All @@ -76,7 +76,7 @@ public function foo(): void
assertType('int', parent::retStaticConst(...)());
assertType('2', $this->retStaticConst(...)());
assertType('bool', X::retStaticConst(...)());
assertType('mixed', $clUnioned->retStaticConst(...)()); // should be bool|int
assertType('mixed', $clUnioned->retStaticConst(...)()); // should be bool|int https://github.com/phpstan/phpstan/issues/11687

assertType('StaticLateBinding\A', A::retStatic());
assertType('StaticLateBinding\B', B::retStatic());
Expand All @@ -85,7 +85,16 @@ public function foo(): void
assertType('static(StaticLateBinding\B)', parent::retStatic());
assertType('static(StaticLateBinding\B)', $this->retStatic());
assertType('bool', X::retStatic());
assertType('bool|StaticLateBinding\A|StaticLateBinding\X', $clUnioned::retStatic()); // should be bool|StaticLateBinding\A
assertType('bool|StaticLateBinding\A|StaticLateBinding\X', $clUnioned::retStatic()); // should be bool|StaticLateBinding\A https://github.com/phpstan/phpstan/issues/11687

assertType('StaticLateBinding\A', A::retStatic(...)());
assertType('StaticLateBinding\B', B::retStatic(...)());
assertType('static(StaticLateBinding\B)', self::retStatic(...)());
assertType('static(StaticLateBinding\B)', static::retStatic(...)());
assertType('static(StaticLateBinding\B)', parent::retStatic(...)());
assertType('static(StaticLateBinding\B)', $this->retStatic(...)());
assertType('bool', X::retStatic(...)());
assertType('mixed', $clUnioned::retStatic(...)()); // should be bool|StaticLateBinding\A https://github.com/phpstan/phpstan/issues/11687

assertType('static(StaticLateBinding\B)', A::retNonStatic());
assertType('static(StaticLateBinding\B)', B::retNonStatic());
Expand All @@ -94,7 +103,7 @@ public function foo(): void
assertType('static(StaticLateBinding\B)', parent::retNonStatic());
assertType('static(StaticLateBinding\B)', $this->retNonStatic());
assertType('bool', X::retNonStatic());
assertType('*ERROR*', $clUnioned->retNonStatic()); // should be bool|static(StaticLateBinding\B)
assertType('*ERROR*', $clUnioned->retNonStatic()); // should be bool|static(StaticLateBinding\B) https://github.com/phpstan/phpstan/issues/11687

A::outStaticConst($v);
assertType('int', $v);
Expand Down

0 comments on commit 9263039

Please sign in to comment.