Skip to content

Commit 9263039

Browse files
authored
Fix late static binding calls for first class callable
1 parent e629cee commit 9263039

File tree

2 files changed

+36
-31
lines changed

2 files changed

+36
-31
lines changed

src/Analyser/MutatingScope.php

+23-27
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,7 @@ private function resolveType(string $exprString, Expr $node): Type
12021202
return new ObjectType(Closure::class);
12031203
}
12041204

1205-
$classType = $this->resolveTypeByName($node->class);
1205+
$classType = $this->resolveTypeByNameWithLateStaticBinding($node->class, $node->name);
12061206
$methodName = $node->name->toString();
12071207
if (!$classType->hasMethod($methodName)->yes()) {
12081208
return new ObjectType(Closure::class);
@@ -2082,19 +2082,7 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
20822082
if ($this->nativeTypesPromoted) {
20832083
$typeCallback = function () use ($node): Type {
20842084
if ($node->class instanceof Name) {
2085-
$staticMethodCalledOnType = $this->resolveTypeByName($node->class);
2086-
if (
2087-
$staticMethodCalledOnType instanceof StaticType
2088-
&& !in_array($node->class->toLowerString(), ['self', 'static', 'parent'], true)
2089-
) {
2090-
$methodReflectionCandidate = $this->getMethodReflection(
2091-
$staticMethodCalledOnType,
2092-
$node->name->name,
2093-
);
2094-
if ($methodReflectionCandidate !== null && $methodReflectionCandidate->isStatic()) {
2095-
$staticMethodCalledOnType = $staticMethodCalledOnType->getStaticObjectType();
2096-
}
2097-
}
2085+
$staticMethodCalledOnType = $this->resolveTypeByNameWithLateStaticBinding($node->class, $node->name);
20982086
} else {
20992087
$staticMethodCalledOnType = $this->getNativeType($node->class);
21002088
}
@@ -2119,19 +2107,7 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
21192107

21202108
$typeCallback = function () use ($node): Type {
21212109
if ($node->class instanceof Name) {
2122-
$staticMethodCalledOnType = $this->resolveTypeByName($node->class);
2123-
if (
2124-
$staticMethodCalledOnType instanceof StaticType
2125-
&& !in_array($node->class->toLowerString(), ['self', 'static', 'parent'], true)
2126-
) {
2127-
$methodReflectionCandidate = $this->getMethodReflection(
2128-
$staticMethodCalledOnType,
2129-
$node->name->name,
2130-
);
2131-
if ($methodReflectionCandidate !== null && $methodReflectionCandidate->isStatic()) {
2132-
$staticMethodCalledOnType = $staticMethodCalledOnType->getStaticObjectType();
2133-
}
2134-
}
2110+
$staticMethodCalledOnType = $this->resolveTypeByNameWithLateStaticBinding($node->class, $node->name);
21352111
} else {
21362112
$staticMethodCalledOnType = TypeCombinator::removeNull($this->getType($node->class))->getObjectTypeOrClassStringObjectType();
21372113
}
@@ -2780,6 +2756,26 @@ public function resolveTypeByName(Name $name): TypeWithClassName
27802756
return new ObjectType($originalClass);
27812757
}
27822758

2759+
private function resolveTypeByNameWithLateStaticBinding(Name $class, Node\Identifier $name): TypeWithClassName
2760+
{
2761+
$classType = $this->resolveTypeByName($class);
2762+
2763+
if (
2764+
$classType instanceof StaticType
2765+
&& !in_array($class->toLowerString(), ['self', 'static', 'parent'], true)
2766+
) {
2767+
$methodReflectionCandidate = $this->getMethodReflection(
2768+
$classType,
2769+
$name->name,
2770+
);
2771+
if ($methodReflectionCandidate !== null && $methodReflectionCandidate->isStatic()) {
2772+
$classType = $classType->getStaticObjectType();
2773+
}
2774+
}
2775+
2776+
return $classType;
2777+
}
2778+
27832779
/**
27842780
* @api
27852781
* @param mixed $value

tests/PHPStan/Analyser/nsrt/static-late-binding.php

+13-4
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public function foo(): void
6767
assertType('int', parent::retStaticConst());
6868
assertType('2', $this->retStaticConst());
6969
assertType('bool', X::retStaticConst());
70-
assertType('*ERROR*', $clUnioned->retStaticConst()); // should be bool|int
70+
assertType('*ERROR*', $clUnioned->retStaticConst()); // should be bool|int https://github.com/phpstan/phpstan/issues/11687
7171

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

8181
assertType('StaticLateBinding\A', A::retStatic());
8282
assertType('StaticLateBinding\B', B::retStatic());
@@ -85,7 +85,16 @@ public function foo(): void
8585
assertType('static(StaticLateBinding\B)', parent::retStatic());
8686
assertType('static(StaticLateBinding\B)', $this->retStatic());
8787
assertType('bool', X::retStatic());
88-
assertType('bool|StaticLateBinding\A|StaticLateBinding\X', $clUnioned::retStatic()); // should be bool|StaticLateBinding\A
88+
assertType('bool|StaticLateBinding\A|StaticLateBinding\X', $clUnioned::retStatic()); // should be bool|StaticLateBinding\A https://github.com/phpstan/phpstan/issues/11687
89+
90+
assertType('StaticLateBinding\A', A::retStatic(...)());
91+
assertType('StaticLateBinding\B', B::retStatic(...)());
92+
assertType('static(StaticLateBinding\B)', self::retStatic(...)());
93+
assertType('static(StaticLateBinding\B)', static::retStatic(...)());
94+
assertType('static(StaticLateBinding\B)', parent::retStatic(...)());
95+
assertType('static(StaticLateBinding\B)', $this->retStatic(...)());
96+
assertType('bool', X::retStatic(...)());
97+
assertType('mixed', $clUnioned::retStatic(...)()); // should be bool|StaticLateBinding\A https://github.com/phpstan/phpstan/issues/11687
8998

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

99108
A::outStaticConst($v);
100109
assertType('int', $v);

0 commit comments

Comments
 (0)