Skip to content

Commit 23faebb

Browse files
committed
resolveType in StaticPropertyFetchHandler
1 parent a275ce9 commit 23faebb

File tree

2 files changed

+92
-63
lines changed

2 files changed

+92
-63
lines changed

src/Analyser/ExprHandler/StaticPropertyFetchHandler.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,29 @@
33
namespace PHPStan\Analyser\ExprHandler;
44

55
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Expr\BinaryOp\Identical;
67
use PhpParser\Node\Expr\StaticPropertyFetch;
8+
use PhpParser\Node\Name;
9+
use PhpParser\Node\Scalar\String_;
710
use PhpParser\Node\Stmt;
11+
use PhpParser\Node\VarLikeIdentifier;
812
use PHPStan\Analyser\ExpressionContext;
913
use PHPStan\Analyser\ExpressionResult;
1014
use PHPStan\Analyser\ExpressionResultStorage;
1115
use PHPStan\Analyser\ExprHandler;
16+
use PHPStan\Analyser\ExprHandler\Helper\NullsafeShortCircuitingHelper;
1217
use PHPStan\Analyser\ImpurePoint;
1318
use PHPStan\Analyser\MutatingScope;
1419
use PHPStan\Analyser\NodeScopeResolver;
1520
use PHPStan\DependencyInjection\AutowiredService;
21+
use PHPStan\Rules\Properties\PropertyReflectionFinder;
22+
use PHPStan\Type\ErrorType;
23+
use PHPStan\Type\MixedType;
24+
use PHPStan\Type\Type;
25+
use PHPStan\Type\TypeCombinator;
26+
use function array_map;
1627
use function array_merge;
28+
use function count;
1729

1830
/**
1931
* @implements ExprHandler<StaticPropertyFetch>
@@ -22,6 +34,12 @@
2234
final class StaticPropertyFetchHandler implements ExprHandler
2335
{
2436

37+
public function __construct(
38+
private PropertyReflectionFinder $propertyReflectionFinder,
39+
)
40+
{
41+
}
42+
2543
public function supports(Expr $expr): bool
2644
{
2745
return $expr instanceof StaticPropertyFetch;
@@ -69,4 +87,77 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
6987
);
7088
}
7189

90+
/**
91+
* @param StaticPropertyFetch $expr
92+
*/
93+
public function resolveType(MutatingScope $scope, Expr $expr): Type
94+
{
95+
if ($expr->name instanceof VarLikeIdentifier) {
96+
if ($scope->nativeTypesPromoted) {
97+
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $scope);
98+
if ($propertyReflection === null) {
99+
return new ErrorType();
100+
}
101+
if (!$propertyReflection->hasNativeType()) {
102+
return new MixedType();
103+
}
104+
105+
$nativeType = $propertyReflection->getNativeType();
106+
107+
if ($expr->class instanceof Expr) {
108+
return NullsafeShortCircuitingHelper::getType($scope, $expr->class, $nativeType);
109+
}
110+
111+
return $nativeType;
112+
}
113+
114+
if ($expr->class instanceof Name) {
115+
$staticPropertyFetchedOnType = $scope->resolveTypeByName($expr->class);
116+
} else {
117+
$staticPropertyFetchedOnType = TypeCombinator::removeNull($scope->getType($expr->class))->getObjectTypeOrClassStringObjectType();
118+
}
119+
120+
$fetchType = $this->propertyFetchType(
121+
$scope,
122+
$staticPropertyFetchedOnType,
123+
$expr->name->toString(),
124+
$expr,
125+
);
126+
if ($fetchType === null) {
127+
$fetchType = new ErrorType();
128+
}
129+
130+
if ($expr->class instanceof Expr) {
131+
return NullsafeShortCircuitingHelper::getType($scope, $expr->class, $fetchType);
132+
}
133+
134+
return $fetchType;
135+
}
136+
137+
$nameType = $scope->getType($expr->name);
138+
if (count($nameType->getConstantStrings()) > 0) {
139+
return TypeCombinator::union(
140+
...array_map(static fn ($constantString) => $constantString->getValue() === '' ? new ErrorType() : $scope
141+
->filterByTruthyValue(new Identical($expr->name, new String_($constantString->getValue())))
142+
->getType(new Expr\StaticPropertyFetch($expr->class, new VarLikeIdentifier($constantString->getValue()))), $nameType->getConstantStrings()),
143+
);
144+
}
145+
146+
return new MixedType();
147+
}
148+
149+
private function propertyFetchType(MutatingScope $scope, Type $fetchedOnType, string $propertyName, StaticPropertyFetch $propertyFetch): ?Type
150+
{
151+
$propertyReflection = $scope->getStaticPropertyReflection($fetchedOnType, $propertyName);
152+
if ($propertyReflection === null) {
153+
return null;
154+
}
155+
156+
if ($scope->isInExpressionAssign($propertyFetch)) {
157+
return $propertyReflection->getWritableType();
158+
}
159+
160+
return $propertyReflection->getReadableType();
161+
}
162+
72163
}

src/Analyser/MutatingScope.php

Lines changed: 1 addition & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ public function __construct(
240240
protected array $inFunctionCallsStack = [],
241241
protected bool $afterExtractCall = false,
242242
private ?self $parentScope = null,
243-
protected bool $nativeTypesPromoted = false,
243+
public bool $nativeTypesPromoted = false,
244244
)
245245
{
246246
if ($namespace === '') {
@@ -1177,13 +1177,6 @@ private function resolveType(string $exprString, Expr $node): Type
11771177
}
11781178
}
11791179

1180-
if ($node instanceof Expr\StaticPropertyFetch) {
1181-
$fetchType = $this->getStaticPropertyFetchType($node);
1182-
if ($fetchType !== null) {
1183-
return $fetchType;
1184-
}
1185-
}
1186-
11871180
if ($node instanceof FuncCall) {
11881181
return $this->getFunctionCallType($node);
11891182
}
@@ -5662,61 +5655,6 @@ private function getFunctionCallType(FuncCall $node): Type
56625655
return $this->transformVoidToNull($parametersAcceptor->getReturnType(), $node);
56635656
}
56645657

5665-
private function getStaticPropertyFetchType(Expr\StaticPropertyFetch $node): ?Type
5666-
{
5667-
if ($node->name instanceof Node\VarLikeIdentifier) {
5668-
if ($this->nativeTypesPromoted) {
5669-
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($node, $this);
5670-
if ($propertyReflection === null) {
5671-
return new ErrorType();
5672-
}
5673-
if (!$propertyReflection->hasNativeType()) {
5674-
return new MixedType();
5675-
}
5676-
5677-
$nativeType = $propertyReflection->getNativeType();
5678-
5679-
if ($node->class instanceof Expr) {
5680-
return NullsafeShortCircuitingHelper::getType($this, $node->class, $nativeType);
5681-
}
5682-
5683-
return $nativeType;
5684-
}
5685-
5686-
if ($node->class instanceof Name) {
5687-
$staticPropertyFetchedOnType = $this->resolveTypeByName($node->class);
5688-
} else {
5689-
$staticPropertyFetchedOnType = TypeCombinator::removeNull($this->getType($node->class))->getObjectTypeOrClassStringObjectType();
5690-
}
5691-
5692-
$fetchType = $this->propertyFetchType(
5693-
$staticPropertyFetchedOnType,
5694-
$node->name->toString(),
5695-
$node,
5696-
);
5697-
if ($fetchType === null) {
5698-
$fetchType = new ErrorType();
5699-
}
5700-
5701-
if ($node->class instanceof Expr) {
5702-
return NullsafeShortCircuitingHelper::getType($this, $node->class, $fetchType);
5703-
}
5704-
5705-
return $fetchType;
5706-
}
5707-
5708-
$nameType = $this->getType($node->name);
5709-
if (count($nameType->getConstantStrings()) > 0) {
5710-
return TypeCombinator::union(
5711-
...array_map(fn ($constantString) => $constantString->getValue() === '' ? new ErrorType() : $this
5712-
->filterByTruthyValue(new BinaryOp\Identical($node->name, new String_($constantString->getValue())))
5713-
->getType(new Expr\StaticPropertyFetch($node->class, new Node\VarLikeIdentifier($constantString->getValue()))), $nameType->getConstantStrings()),
5714-
);
5715-
}
5716-
5717-
return null;
5718-
}
5719-
57205658
private function getPropertyFetchType(PropertyFetch $node): ?Type
57215659
{
57225660
if ($node->name instanceof Node\Identifier) {

0 commit comments

Comments
 (0)