Skip to content

Commit fdd2dfa

Browse files
committed
Take advantage of NodeScopeResolver being a parameter of processExpr
1 parent 64870b4 commit fdd2dfa

File tree

4 files changed

+125
-65
lines changed

4 files changed

+125
-65
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Expr\ArrowFunction;
7+
use PhpParser\Node\Stmt;
8+
use PHPStan\Analyser\ExpressionContext;
9+
use PHPStan\Analyser\ExpressionResult;
10+
use PHPStan\Analyser\ExpressionResultStorage;
11+
use PHPStan\Analyser\ExprHandler;
12+
use PHPStan\Analyser\MutatingScope;
13+
use PHPStan\Analyser\NodeScopeResolver;
14+
use PHPStan\DependencyInjection\AutowiredService;
15+
16+
/**
17+
* @implements ExprHandler<ArrowFunction>
18+
*/
19+
#[AutowiredService]
20+
final class ArrowFunctionHandler implements ExprHandler
21+
{
22+
23+
public function supports(Expr $expr): bool
24+
{
25+
return $expr instanceof ArrowFunction;
26+
}
27+
28+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
29+
{
30+
$result = $nodeScopeResolver->processArrowFunctionNode($stmt, $expr, $scope, $storage, $nodeCallback, null);
31+
32+
return new ExpressionResult(
33+
$result->getScope(),
34+
hasYield: $result->hasYield(),
35+
isAlwaysTerminating: false,
36+
throwPoints: [],
37+
impurePoints: [],
38+
);
39+
}
40+
41+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Expr\Closure;
7+
use PhpParser\Node\Stmt;
8+
use PHPStan\Analyser\ExpressionContext;
9+
use PHPStan\Analyser\ExpressionResult;
10+
use PHPStan\Analyser\ExpressionResultStorage;
11+
use PHPStan\Analyser\ExprHandler;
12+
use PHPStan\Analyser\MutatingScope;
13+
use PHPStan\Analyser\NodeScopeResolver;
14+
use PHPStan\DependencyInjection\AutowiredService;
15+
16+
/**
17+
* @implements ExprHandler<Closure>
18+
*/
19+
#[AutowiredService]
20+
final class ClosureHandler implements ExprHandler
21+
{
22+
23+
public function supports(Expr $expr): bool
24+
{
25+
return $expr instanceof Closure;
26+
}
27+
28+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
29+
{
30+
$processClosureResult = $nodeScopeResolver->processClosureNode($stmt, $expr, $scope, $storage, $nodeCallback, $context, null);
31+
$scope = $processClosureResult->applyByRefUseScope($processClosureResult->getScope());
32+
33+
return new ExpressionResult(
34+
$scope,
35+
hasYield: false,
36+
isAlwaysTerminating: false,
37+
throwPoints: [],
38+
impurePoints: [],
39+
);
40+
}
41+
42+
}

src/Analyser/ExprHandler/MethodCallHandler.php

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use PHPStan\Type\MixedType;
3232
use PHPStan\Type\NeverType;
3333
use PHPStan\Type\ObjectType;
34+
use PHPStan\Type\TypeUtils;
3435
use ReflectionFunction;
3536
use ReflectionMethod;
3637
use Throwable;
@@ -180,7 +181,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
180181
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
181182
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
182183

183-
return new ExpressionResult(
184+
$result = new ExpressionResult(
184185
$scope,
185186
hasYield: $hasYield,
186187
isAlwaysTerminating: $isAlwaysTerminating,
@@ -189,6 +190,44 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
189190
truthyScopeCallback: static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
190191
falseyScopeCallback: static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
191192
);
193+
194+
return $this->processInitializedProperties($nodeScopeResolver, $expr, $originalScope, $result);
195+
}
196+
197+
private function processInitializedProperties(NodeScopeResolver $nodeScopeResolver, MethodCall $expr, MutatingScope $originalScope, ExpressionResult $handlerResult): ExpressionResult
198+
{
199+
$scope = $handlerResult->getScope();
200+
$calledOnType = $originalScope->getType($expr->var);
201+
if ($expr->name instanceof Expr) {
202+
return $handlerResult;
203+
}
204+
$methodName = $expr->name->name;
205+
$methodReflection = $originalScope->getMethodReflection($calledOnType, $methodName);
206+
if ($methodReflection === null) {
207+
return $handlerResult;
208+
}
209+
if (
210+
$scope->isInClass()
211+
&& $scope->getClassReflection()->getName() === $methodReflection->getDeclaringClass()->getName()
212+
&& ($scope->getFunctionName() !== null && strtolower($scope->getFunctionName()) === '__construct')
213+
&& TypeUtils::findThisType($calledOnType) !== null
214+
) {
215+
$calledMethodScope = $nodeScopeResolver->processCalledMethod($methodReflection);
216+
if ($calledMethodScope !== null) {
217+
$scope = $scope->mergeInitializedProperties($calledMethodScope);
218+
return new ExpressionResult(
219+
$scope,
220+
hasYield: $handlerResult->hasYield(),
221+
isAlwaysTerminating: $handlerResult->isAlwaysTerminating(),
222+
throwPoints: $handlerResult->getThrowPoints(),
223+
impurePoints: $handlerResult->getImpurePoints(),
224+
truthyScopeCallback: static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
225+
falseyScopeCallback: static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
226+
);
227+
}
228+
}
229+
230+
return $handlerResult;
192231
}
193232

194233
private function getMethodThrowPoint(MethodReflection $methodReflection, ParametersAcceptor $parametersAcceptor, MethodCall $methodCall, MutatingScope $scope): ?InternalThrowPoint

src/Analyser/NodeScopeResolver.php

Lines changed: 2 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2528,36 +2528,10 @@ public function processExprNode(
25282528
continue;
25292529
}
25302530

2531-
$handlerResult = $exprHandler->processExpr($this, $stmt, $expr, $scope, $storage, $nodeCallback, $context);
2532-
2533-
if ($expr instanceof MethodCall) {
2534-
$handlerResult = $this->processMethodCallInitializedProperties($expr, $scope, $handlerResult);
2535-
}
2536-
2537-
return $handlerResult;
2531+
return $exprHandler->processExpr($this, $stmt, $expr, $scope, $storage, $nodeCallback, $context);
25382532
}
25392533

2540-
if ($expr instanceof Expr\Closure) {
2541-
$processClosureResult = $this->processClosureNode($stmt, $expr, $scope, $storage, $nodeCallback, $context, null);
2542-
$scope = $processClosureResult->applyByRefUseScope($processClosureResult->getScope());
2543-
2544-
return new ExpressionResult(
2545-
$scope,
2546-
false,
2547-
false,
2548-
[],
2549-
[],
2550-
);
2551-
} elseif ($expr instanceof Expr\ArrowFunction) {
2552-
$result = $this->processArrowFunctionNode($stmt, $expr, $scope, $storage, $nodeCallback, null);
2553-
return new ExpressionResult(
2554-
$result->getScope(),
2555-
$result->hasYield(),
2556-
false,
2557-
[],
2558-
[],
2559-
);
2560-
} elseif ($expr instanceof List_) {
2534+
if ($expr instanceof List_) {
25612535
// only in assign and foreach, processed elsewhere
25622536
return new ExpressionResult($scope, hasYield: false, isAlwaysTerminating: false, throwPoints: [], impurePoints: []);
25632537
} elseif ($expr instanceof AlwaysRememberedExpr) {
@@ -5027,42 +5001,6 @@ private function processNodesForTraitUse($node, ClassReflection $traitReflection
50275001
}
50285002
}
50295003

5030-
private function processMethodCallInitializedProperties(MethodCall $expr, MutatingScope $originalScope, ExpressionResult $handlerResult): ExpressionResult
5031-
{
5032-
$scope = $handlerResult->getScope();
5033-
$calledOnType = $originalScope->getType($expr->var);
5034-
if ($expr->name instanceof Expr) {
5035-
return $handlerResult;
5036-
}
5037-
$methodName = $expr->name->name;
5038-
$methodReflection = $originalScope->getMethodReflection($calledOnType, $methodName);
5039-
if ($methodReflection === null) {
5040-
return $handlerResult;
5041-
}
5042-
if (
5043-
$scope->isInClass()
5044-
&& $scope->getClassReflection()->getName() === $methodReflection->getDeclaringClass()->getName()
5045-
&& ($scope->getFunctionName() !== null && strtolower($scope->getFunctionName()) === '__construct')
5046-
&& TypeUtils::findThisType($calledOnType) !== null
5047-
) {
5048-
$calledMethodScope = $this->processCalledMethod($methodReflection);
5049-
if ($calledMethodScope !== null) {
5050-
$scope = $scope->mergeInitializedProperties($calledMethodScope);
5051-
return new ExpressionResult(
5052-
$scope,
5053-
hasYield: $handlerResult->hasYield(),
5054-
isAlwaysTerminating: $handlerResult->isAlwaysTerminating(),
5055-
throwPoints: $handlerResult->getThrowPoints(),
5056-
impurePoints: $handlerResult->getImpurePoints(),
5057-
truthyScopeCallback: static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
5058-
falseyScopeCallback: static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
5059-
);
5060-
}
5061-
}
5062-
5063-
return $handlerResult;
5064-
}
5065-
50665004
public function processCalledMethod(MethodReflection $methodReflection): ?MutatingScope
50675005
{
50685006
$declaringClass = $methodReflection->getDeclaringClass();

0 commit comments

Comments
 (0)