|
10 | 10 | use PhpParser\Node\Expr\Array_; |
11 | 11 | use PhpParser\Node\Expr\ArrayDimFetch; |
12 | 12 | use PhpParser\Node\Expr\BinaryOp\BooleanAnd; |
| 13 | +use PhpParser\Node\Expr\CallLike; |
13 | 14 | use PhpParser\Node\Expr\Closure; |
14 | 15 | use PhpParser\Node\Expr\Isset_; |
15 | 16 | use PhpParser\Node\Expr\PostDec; |
|
19 | 20 | use PhpParser\Node\Expr\PropertyFetch; |
20 | 21 | use PhpParser\Node\Expr\StaticPropertyFetch; |
21 | 22 | use PhpParser\Node\Expr\Variable; |
| 23 | +use PhpParser\Node\Identifier; |
22 | 24 | use PhpParser\Node\Name\FullyQualified; |
23 | 25 | use PhpParser\Node\Param; |
24 | 26 | use PhpParser\Node\Stmt\Break_; |
|
36 | 38 | use PhpParser\Node\Stmt\While_; |
37 | 39 | use PhpParser\NodeVisitor; |
38 | 40 | use PhpParser\NodeVisitorAbstract; |
| 41 | +use PHPStan\Analyser\Scope; |
| 42 | +use PHPStan\Reflection\FunctionReflection; |
| 43 | +use PHPStan\Reflection\MethodReflection; |
| 44 | +use PHPStan\Type\ArrayType; |
39 | 45 | use Rector\Contract\PhpParser\DecoratingNodeVisitorInterface; |
40 | 46 | use Rector\NodeTypeResolver\Node\AttributeKey; |
| 47 | +use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper; |
41 | 48 | use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; |
42 | 49 | use Rector\PhpParser\NodeTraverser\SimpleNodeTraverser; |
| 50 | +use Rector\Reflection\ReflectionResolver; |
43 | 51 |
|
44 | 52 | final class ContextNodeVisitor extends NodeVisitorAbstract implements DecoratingNodeVisitorInterface |
45 | 53 | { |
46 | 54 | public function __construct( |
47 | | - private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser |
| 55 | + private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser, |
| 56 | + private readonly ReflectionResolver $reflectionResolver |
48 | 57 | ) { |
49 | 58 | } |
50 | 59 |
|
@@ -92,6 +101,49 @@ public function enterNode(Node $node): ?Node |
92 | 101 | return null; |
93 | 102 | } |
94 | 103 |
|
| 104 | + if ($node instanceof CallLike && ! $node->isFirstClassCallable()) { |
| 105 | + $functionReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); |
| 106 | + if (! $functionReflection instanceof FunctionReflection && ! $functionReflection instanceof MethodReflection) { |
| 107 | + return null; |
| 108 | + } |
| 109 | + |
| 110 | + $scope = $node->getAttribute(AttributeKey::SCOPE); |
| 111 | + if (! $scope instanceof Scope) { |
| 112 | + return null; |
| 113 | + } |
| 114 | + |
| 115 | + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select( |
| 116 | + $functionReflection, |
| 117 | + $node, |
| 118 | + $scope |
| 119 | + ); |
| 120 | + |
| 121 | + $args = $node->getArgs(); |
| 122 | + foreach ($parametersAcceptor->getParameters() as $key => $parameterReflection) { |
| 123 | + // also process maybe callable |
| 124 | + if ($parameterReflection->getType()->isCallable()->no()) { |
| 125 | + continue; |
| 126 | + } |
| 127 | + |
| 128 | + if ($parameterReflection->getType() instanceof ArrayType) { |
| 129 | + continue; |
| 130 | + } |
| 131 | + |
| 132 | + // based on name |
| 133 | + foreach ($args as $arg) { |
| 134 | + if ($arg->name instanceof Identifier && $parameterReflection->getName() === $arg->name->toString()) { |
| 135 | + $arg->value->setAttribute(AttributeKey::IS_ARG_VALUE_CALLABLE, true); |
| 136 | + continue 2; |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + // based on key |
| 141 | + if (isset($args[$key])) { |
| 142 | + $args[$key]->value->setAttribute(AttributeKey::IS_ARG_VALUE_CALLABLE, true); |
| 143 | + } |
| 144 | + } |
| 145 | + } |
| 146 | + |
95 | 147 | if ($node instanceof Arg) { |
96 | 148 | $node->value->setAttribute(AttributeKey::IS_ARG_VALUE, true); |
97 | 149 | return null; |
|
0 commit comments