Skip to content

Commit 07f3f7a

Browse files
committed
Introduce *CallableNodeHandler by extracting the processing from NodeScopeResolver
1 parent 06417d4 commit 07f3f7a

5 files changed

Lines changed: 263 additions & 75 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\FunctionCallableNode;
15+
use PHPStan\Type\MixedType;
16+
use PHPStan\Type\Type;
17+
18+
/**
19+
* @implements ExprHandler<FunctionCallableNode>
20+
*/
21+
#[AutowiredService]
22+
final class FunctionCallableNodeHandler implements ExprHandler
23+
{
24+
25+
public function supports(Expr $expr): bool
26+
{
27+
return $expr instanceof FunctionCallableNode;
28+
}
29+
30+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
31+
{
32+
$throwPoints = [];
33+
$impurePoints = [];
34+
$hasYield = false;
35+
$isAlwaysTerminating = false;
36+
if ($expr->getName() instanceof Expr) {
37+
$result = $nodeScopeResolver->processExprNode($stmt, $expr->getName(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
38+
$scope = $result->getScope();
39+
$hasYield = $result->hasYield();
40+
$throwPoints = $result->getThrowPoints();
41+
$impurePoints = $result->getImpurePoints();
42+
$isAlwaysTerminating = $result->isAlwaysTerminating();
43+
}
44+
45+
return new ExpressionResult(
46+
$scope,
47+
hasYield: $hasYield,
48+
isAlwaysTerminating: $isAlwaysTerminating,
49+
throwPoints: $throwPoints,
50+
impurePoints: $impurePoints,
51+
);
52+
}
53+
54+
public function resolveType(MutatingScope $scope, Expr $expr): Type
55+
{
56+
// in practice the type of the first-class callable is resolved
57+
// by FirstClassCallableFuncCallHandler
58+
return new MixedType();
59+
}
60+
61+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\InstantiationCallableNode;
15+
use PHPStan\Type\MixedType;
16+
use PHPStan\Type\Type;
17+
18+
/**
19+
* @implements ExprHandler<InstantiationCallableNode>
20+
*/
21+
#[AutowiredService]
22+
final class InstantiationCallableNodeHandler implements ExprHandler
23+
{
24+
25+
public function supports(Expr $expr): bool
26+
{
27+
return $expr instanceof InstantiationCallableNode;
28+
}
29+
30+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
31+
{
32+
$throwPoints = [];
33+
$impurePoints = [];
34+
$hasYield = false;
35+
$isAlwaysTerminating = false;
36+
if ($expr->getClass() instanceof Expr) {
37+
$classResult = $nodeScopeResolver->processExprNode($stmt, $expr->getClass(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
38+
$scope = $classResult->getScope();
39+
$hasYield = $classResult->hasYield();
40+
$throwPoints = $classResult->getThrowPoints();
41+
$impurePoints = $classResult->getImpurePoints();
42+
$isAlwaysTerminating = $classResult->isAlwaysTerminating();
43+
}
44+
45+
return new ExpressionResult(
46+
$scope,
47+
hasYield: $hasYield,
48+
isAlwaysTerminating: $isAlwaysTerminating,
49+
throwPoints: $throwPoints,
50+
impurePoints: $impurePoints,
51+
);
52+
}
53+
54+
public function resolveType(MutatingScope $scope, Expr $expr): Type
55+
{
56+
// in practice the type of the first-class callable is resolved
57+
// by FirstClassCallableNewHandler
58+
return new MixedType();
59+
}
60+
61+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\MethodCallableNode;
15+
use PHPStan\Type\MixedType;
16+
use PHPStan\Type\Type;
17+
use function array_merge;
18+
19+
/**
20+
* @implements ExprHandler<MethodCallableNode>
21+
*/
22+
#[AutowiredService]
23+
final class MethodCallableNodeHandler implements ExprHandler
24+
{
25+
26+
public function supports(Expr $expr): bool
27+
{
28+
return $expr instanceof MethodCallableNode;
29+
}
30+
31+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
32+
{
33+
$result = $nodeScopeResolver->processExprNode($stmt, $expr->getVar(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
34+
$scope = $result->getScope();
35+
$hasYield = $result->hasYield();
36+
$throwPoints = $result->getThrowPoints();
37+
$impurePoints = $result->getImpurePoints();
38+
$isAlwaysTerminating = false;
39+
if ($expr->getName() instanceof Expr) {
40+
$nameResult = $nodeScopeResolver->processExprNode($stmt, $expr->getName(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
41+
$scope = $nameResult->getScope();
42+
$hasYield = $hasYield || $nameResult->hasYield();
43+
$throwPoints = array_merge($throwPoints, $nameResult->getThrowPoints());
44+
$impurePoints = array_merge($impurePoints, $nameResult->getImpurePoints());
45+
$isAlwaysTerminating = $nameResult->isAlwaysTerminating();
46+
}
47+
48+
return new ExpressionResult(
49+
$scope,
50+
hasYield: $hasYield,
51+
isAlwaysTerminating: $isAlwaysTerminating,
52+
throwPoints: $throwPoints,
53+
impurePoints: $impurePoints,
54+
);
55+
}
56+
57+
public function resolveType(MutatingScope $scope, Expr $expr): Type
58+
{
59+
// in practice the type of the first-class callable is resolved
60+
// by FirstClassCallableMethodCallHandler
61+
return new MixedType();
62+
}
63+
64+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\ExprHandler\Virtual;
4+
5+
use PhpParser\Node\Expr;
6+
use PhpParser\Node\Stmt;
7+
use PHPStan\Analyser\ExpressionContext;
8+
use PHPStan\Analyser\ExpressionResult;
9+
use PHPStan\Analyser\ExpressionResultStorage;
10+
use PHPStan\Analyser\ExprHandler;
11+
use PHPStan\Analyser\MutatingScope;
12+
use PHPStan\Analyser\NodeScopeResolver;
13+
use PHPStan\DependencyInjection\AutowiredService;
14+
use PHPStan\Node\StaticMethodCallableNode;
15+
use PHPStan\Type\MixedType;
16+
use PHPStan\Type\Type;
17+
use function array_merge;
18+
19+
/**
20+
* @implements ExprHandler<StaticMethodCallableNode>
21+
*/
22+
#[AutowiredService]
23+
final class StaticMethodCallableNodeHandler implements ExprHandler
24+
{
25+
26+
public function supports(Expr $expr): bool
27+
{
28+
return $expr instanceof StaticMethodCallableNode;
29+
}
30+
31+
public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
32+
{
33+
$throwPoints = [];
34+
$impurePoints = [];
35+
$hasYield = false;
36+
$isAlwaysTerminating = false;
37+
if ($expr->getClass() instanceof Expr) {
38+
$classResult = $nodeScopeResolver->processExprNode($stmt, $expr->getClass(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
39+
$scope = $classResult->getScope();
40+
$hasYield = $classResult->hasYield();
41+
$throwPoints = $classResult->getThrowPoints();
42+
$impurePoints = $classResult->getImpurePoints();
43+
$isAlwaysTerminating = $classResult->isAlwaysTerminating();
44+
}
45+
if ($expr->getName() instanceof Expr) {
46+
$nameResult = $nodeScopeResolver->processExprNode($stmt, $expr->getName(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
47+
$scope = $nameResult->getScope();
48+
$hasYield = $hasYield || $nameResult->hasYield();
49+
$throwPoints = array_merge($throwPoints, $nameResult->getThrowPoints());
50+
$impurePoints = array_merge($impurePoints, $nameResult->getImpurePoints());
51+
$isAlwaysTerminating = $isAlwaysTerminating || $nameResult->isAlwaysTerminating();
52+
}
53+
54+
return new ExpressionResult(
55+
$scope,
56+
hasYield: $hasYield,
57+
isAlwaysTerminating: $isAlwaysTerminating,
58+
throwPoints: $throwPoints,
59+
impurePoints: $impurePoints,
60+
);
61+
}
62+
63+
public function resolveType(MutatingScope $scope, Expr $expr): Type
64+
{
65+
// in practice the type of the first-class callable is resolved
66+
// by FirstClassCallableStaticCallHandler
67+
return new MixedType();
68+
}
69+
70+
}

src/Analyser/NodeScopeResolver.php

Lines changed: 7 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2510,85 +2510,17 @@ public function processExprNode(
25102510
if ($expr instanceof List_) {
25112511
// only in assign and foreach, processed elsewhere
25122512
return new ExpressionResult($scope, hasYield: false, isAlwaysTerminating: false, throwPoints: [], impurePoints: []);
2513-
} elseif ($expr instanceof FunctionCallableNode) {
2514-
$throwPoints = [];
2515-
$impurePoints = [];
2516-
$hasYield = false;
2517-
$isAlwaysTerminating = false;
2518-
if ($expr->getName() instanceof Expr) {
2519-
$result = $this->processExprNode($stmt, $expr->getName(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
2520-
$scope = $result->getScope();
2521-
$hasYield = $result->hasYield();
2522-
$throwPoints = $result->getThrowPoints();
2523-
$impurePoints = $result->getImpurePoints();
2524-
$isAlwaysTerminating = $result->isAlwaysTerminating();
2525-
}
2526-
} elseif ($expr instanceof MethodCallableNode) {
2527-
$result = $this->processExprNode($stmt, $expr->getVar(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
2528-
$scope = $result->getScope();
2529-
$hasYield = $result->hasYield();
2530-
$throwPoints = $result->getThrowPoints();
2531-
$impurePoints = $result->getImpurePoints();
2532-
$isAlwaysTerminating = false;
2533-
if ($expr->getName() instanceof Expr) {
2534-
$nameResult = $this->processExprNode($stmt, $expr->getName(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
2535-
$scope = $nameResult->getScope();
2536-
$hasYield = $hasYield || $nameResult->hasYield();
2537-
$throwPoints = array_merge($throwPoints, $nameResult->getThrowPoints());
2538-
$impurePoints = array_merge($impurePoints, $nameResult->getImpurePoints());
2539-
$isAlwaysTerminating = $nameResult->isAlwaysTerminating();
2540-
}
2541-
} elseif ($expr instanceof StaticMethodCallableNode) {
2542-
$throwPoints = [];
2543-
$impurePoints = [];
2544-
$hasYield = false;
2545-
$isAlwaysTerminating = false;
2546-
if ($expr->getClass() instanceof Expr) {
2547-
$classResult = $this->processExprNode($stmt, $expr->getClass(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
2548-
$scope = $classResult->getScope();
2549-
$hasYield = $classResult->hasYield();
2550-
$throwPoints = $classResult->getThrowPoints();
2551-
$impurePoints = $classResult->getImpurePoints();
2552-
$isAlwaysTerminating = $classResult->isAlwaysTerminating();
2553-
}
2554-
if ($expr->getName() instanceof Expr) {
2555-
$nameResult = $this->processExprNode($stmt, $expr->getName(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
2556-
$scope = $nameResult->getScope();
2557-
$hasYield = $hasYield || $nameResult->hasYield();
2558-
$throwPoints = array_merge($throwPoints, $nameResult->getThrowPoints());
2559-
$impurePoints = array_merge($impurePoints, $nameResult->getImpurePoints());
2560-
$isAlwaysTerminating = $isAlwaysTerminating || $nameResult->isAlwaysTerminating();
2561-
}
2562-
} elseif ($expr instanceof InstantiationCallableNode) {
2563-
$throwPoints = [];
2564-
$impurePoints = [];
2565-
$hasYield = false;
2566-
$isAlwaysTerminating = false;
2567-
if ($expr->getClass() instanceof Expr) {
2568-
$classResult = $this->processExprNode($stmt, $expr->getClass(), $scope, $storage, $nodeCallback, ExpressionContext::createDeep());
2569-
$scope = $classResult->getScope();
2570-
$hasYield = $classResult->hasYield();
2571-
$throwPoints = $classResult->getThrowPoints();
2572-
$impurePoints = $classResult->getImpurePoints();
2573-
$isAlwaysTerminating = $classResult->isAlwaysTerminating();
2574-
}
2575-
} else {
2576-
$hasYield = false;
2577-
$throwPoints = [];
2578-
$impurePoints = [];
2579-
$isAlwaysTerminating = false;
25802513
}
25812514

2582-
$result = new ExpressionResult(
2515+
return new ExpressionResult(
25832516
$scope,
2584-
$hasYield,
2585-
$isAlwaysTerminating,
2586-
$throwPoints,
2587-
$impurePoints,
2588-
static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
2589-
static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
2517+
hasYield: false,
2518+
isAlwaysTerminating: false,
2519+
throwPoints: [],
2520+
impurePoints: [],
2521+
truthyScopeCallback: static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
2522+
falseyScopeCallback: static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
25902523
);
2591-
return $result;
25922524
}
25932525

25942526
/**

0 commit comments

Comments
 (0)