Skip to content

Commit 0bbb4c7

Browse files
[Php81] Skip as Arg of non-native function call on FunctionLikeToFirstClassCallableRector (#7592)
* [Php81] Skip as Arg of non-native function call on FunctionLikeToFirstClassCallableRector * fix * clean up * [ci-review] Rector Rectify --------- Co-authored-by: GitHub Action <[email protected]>
1 parent 758adc7 commit 0bbb4c7

File tree

3 files changed

+30
-174
lines changed

3 files changed

+30
-174
lines changed

rules-tests/CodingStyle/Rector/FunctionLike/FunctionLikeToFirstClassCallableRector/Fixture/with_trailing_comma.php.inc

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,13 @@ final class WithTrailingComma
88
{
99
$array = [1, 2, 3];
1010

11-
$this->map(
12-
$array,
11+
array_map(
1312
fn($item) => var_dump(
1413
$item,
15-
)
14+
),
15+
$array
1616
);
1717
}
18-
19-
private function map(array $items, callable $callback): void {
20-
foreach($items as $i) {
21-
$callback($i);
22-
}
23-
}
2418
}
2519

2620
?>
@@ -35,19 +29,13 @@ final class WithTrailingComma
3529
{
3630
$array = [1, 2, 3];
3731

38-
$this->map(
39-
$array,
32+
array_map(
4033
var_dump(
4134
...
42-
)
35+
),
36+
$array
4337
);
4438
}
45-
46-
private function map(array $items, callable $callback): void {
47-
foreach($items as $i) {
48-
$callback($i);
49-
}
50-
}
5139
}
5240

5341
?>

rules/CodingStyle/Rector/FunctionLike/FunctionLikeToFirstClassCallableRector.php

Lines changed: 10 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Rector\CodingStyle\Rector\FunctionLike;
66

7-
use PhpParser\Comment\Doc;
87
use PhpParser\Node;
98
use PhpParser\Node\Arg;
109
use PhpParser\Node\Expr;
@@ -14,7 +13,6 @@
1413
use PhpParser\Node\Expr\Closure;
1514
use PhpParser\Node\Expr\FuncCall;
1615
use PhpParser\Node\Expr\MethodCall;
17-
use PhpParser\Node\Expr\New_;
1816
use PhpParser\Node\Expr\StaticCall;
1917
use PhpParser\Node\Expr\Variable;
2018
use PhpParser\Node\FunctionLike;
@@ -24,19 +22,9 @@
2422
use PhpParser\Node\VariadicPlaceholder;
2523
use PhpParser\NodeVisitor;
2624
use PHPStan\Analyser\Scope;
27-
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
2825
use PHPStan\Reflection\Annotations\AnnotationMethodReflection;
29-
use PHPStan\Reflection\ExtendedFunctionVariant;
3026
use PHPStan\Reflection\Native\NativeFunctionReflection;
31-
use PHPStan\Reflection\ParametersAcceptorSelector;
32-
use PHPStan\Reflection\ResolvedFunctionVariantWithOriginal;
33-
use PHPStan\Type\CallableType;
34-
use PHPStan\Type\ObjectType;
35-
use PHPStan\Type\UnionType;
36-
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
37-
use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper;
3827
use Rector\PhpParser\AstResolver;
39-
use Rector\PhpParser\Node\BetterNodeFinder;
4028
use Rector\PHPStan\ScopeFetcher;
4129
use Rector\Rector\AbstractRector;
4230
use Rector\Reflection\ReflectionResolver;
@@ -62,9 +50,7 @@ final class FunctionLikeToFirstClassCallableRector extends AbstractRector implem
6250

6351
public function __construct(
6452
private readonly AstResolver $astResolver,
65-
private readonly ReflectionResolver $reflectionResolver,
66-
private readonly BetterNodeFinder $betterNodeFinder,
67-
private readonly PhpDocInfoFactory $phpDocInfoFactory
53+
private readonly ReflectionResolver $reflectionResolver
6854
) {
6955
}
7056

@@ -89,19 +75,11 @@ function ($parameter) {
8975

9076
public function getNodeTypes(): array
9177
{
92-
return [
93-
Assign::class,
94-
MethodCall::class,
95-
FuncCall::class,
96-
StaticCall::class,
97-
New_::class,
98-
ArrowFunction::class,
99-
Closure::class,
100-
];
78+
return [Assign::class, CallLike::class, ArrowFunction::class, Closure::class];
10179
}
10280

10381
/**
104-
* @param MethodCall|FuncCall|StaticCall|New_|ArrowFunction|Closure $node
82+
* @param CallLike|ArrowFunction|Closure $node
10583
*/
10684
public function refactor(Node $node): null|CallLike
10785
{
@@ -118,99 +96,14 @@ public function refactor(Node $node): null|CallLike
11896
return null;
11997
}
12098

121-
$args = $node->getArgs();
122-
foreach ($args as $key => $arg) {
123-
if ($arg->value instanceof Closure || $arg->value instanceof ArrowFunction) {
124-
// verify caller signature
125-
$methodReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node);
126-
127-
if ($methodReflection === null) {
128-
return null;
129-
}
130-
131-
$reflection = ParametersAcceptorSelectorVariantsWrapper::select(
132-
$methodReflection,
133-
$node,
134-
ScopeFetcher::fetch($node)
135-
);
136-
137-
if ($reflection instanceof ResolvedFunctionVariantWithOriginal) {
138-
$reflection = ParametersAcceptorSelector::combineAcceptors(
139-
$methodReflection->getVariants()
140-
);
141-
142-
if (! $reflection instanceof ExtendedFunctionVariant) {
143-
return null;
144-
}
145-
}
146-
147-
$classMethodOrFunction = $this->astResolver->resolveClassMethodOrFunctionFromCall($node);
148-
if (! $classMethodOrFunction instanceof FunctionLike) {
149-
return null;
150-
}
99+
$methodReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node);
100+
if ($methodReflection instanceof NativeFunctionReflection) {
101+
return null;
102+
}
151103

152-
foreach ($reflection->getParameters() as $index => $parameterReflection) {
153-
if ($index !== $key) {
154-
continue;
155-
}
156-
157-
if ($parameterReflection->getType() instanceof CallableType
158-
&&
159-
count($parameterReflection->getType()->getParameters()) !== 1
160-
&& ! $methodReflection instanceof NativeFunctionReflection
161-
&& $this->hasDocCommentForCallable($classMethodOrFunction, $index)
162-
) {
163-
$args[$key]->value->setAttribute(self::HAS_CALLBACK_SIGNATURE_MULTI_PARAMS, true);
164-
return null;
165-
}
166-
167-
$parameterName = $parameterReflection->getName();
168-
169-
$isInvokable = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped(
170-
$classMethodOrFunction,
171-
fn (Node $node): bool => $node instanceof FuncCall
172-
&& $node->name instanceof Variable
173-
&& $this->isName($node->name, $parameterName)
174-
&& count($node->args) > 1
175-
);
176-
177-
if ($isInvokable) {
178-
$args[$key]->value->setAttribute(self::HAS_CALLBACK_SIGNATURE_MULTI_PARAMS, true);
179-
return null;
180-
}
181-
182-
$isClosureBindTo = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped(
183-
$classMethodOrFunction,
184-
function (Node $node) use ($parameterName): bool {
185-
if (! $node instanceof MethodCall) {
186-
return false;
187-
}
188-
189-
if (! $node->name instanceof Identifier) {
190-
return false;
191-
}
192-
193-
if (! $this->isName($node->name, 'bindTo')) {
194-
return false;
195-
}
196-
197-
if (! $node->var instanceof Variable) {
198-
return false;
199-
}
200-
201-
if (! $this->isObjectType($node->var, new ObjectType('Closure'))) {
202-
return false;
203-
}
204-
205-
return $this->isName($node->var, $parameterName);
206-
}
207-
);
208-
209-
if ($isClosureBindTo) {
210-
$args[$key]->value->setAttribute(self::HAS_CALLBACK_SIGNATURE_MULTI_PARAMS, true);
211-
return null;
212-
}
213-
}
104+
foreach ($node->getArgs() as $arg) {
105+
if ($arg->value instanceof Closure || $arg->value instanceof ArrowFunction) {
106+
$arg->value->setAttribute(self::HAS_CALLBACK_SIGNATURE_MULTI_PARAMS, true);
214107
}
215108
}
216109

@@ -237,37 +130,6 @@ public function provideMinPhpVersion(): int
237130
return PhpVersionFeature::FIRST_CLASS_CALLABLE_SYNTAX;
238131
}
239132

240-
private function hasDocCommentForCallable(FunctionLike $functionLike, int $index): bool
241-
{
242-
$docComment = $functionLike->getDocComment();
243-
if (! $docComment instanceof Doc) {
244-
return false;
245-
}
246-
247-
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($functionLike);
248-
$params = $functionLike->getParams();
249-
250-
$paramName = null;
251-
foreach ($params as $key => $param) {
252-
if ($key === $index) {
253-
$paramName = (string) $this->getName($param);
254-
break;
255-
}
256-
}
257-
258-
if ($paramName === null) {
259-
return false;
260-
}
261-
262-
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName);
263-
if ($paramTagValueNode instanceof ParamTagValueNode) {
264-
$type = $phpDocInfo->getParamType($paramName);
265-
return ($type instanceof CallableType && count($type->getParameters()) !== 1) || $type instanceof UnionType;
266-
}
267-
268-
return false;
269-
}
270-
271133
private function shouldSkip(
272134
ArrowFunction|Closure $node,
273135
FuncCall|MethodCall|StaticCall $callLike,

src/Reflection/ReflectionResolver.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Rector\Reflection;
66

77
use PhpParser\Node;
8+
use PhpParser\Node\Expr\CallLike;
89
use PhpParser\Node\Expr\FuncCall;
910
use PhpParser\Node\Expr\MethodCall;
1011
use PhpParser\Node\Expr\New_;
@@ -192,21 +193,26 @@ public function resolveMethodReflectionFromMethodCall(MethodCall $methodCall): ?
192193
}
193194

194195
public function resolveFunctionLikeReflectionFromCall(
195-
MethodCall|FuncCall|StaticCall|New_ $call
196+
CallLike $callLike
196197
): MethodReflection | FunctionReflection | null {
197-
if ($call instanceof MethodCall) {
198-
return $this->resolveMethodReflectionFromMethodCall($call);
198+
if ($callLike instanceof MethodCall) {
199+
return $this->resolveMethodReflectionFromMethodCall($callLike);
199200
}
200201

201-
if ($call instanceof StaticCall) {
202-
return $this->resolveMethodReflectionFromStaticCall($call);
202+
if ($callLike instanceof StaticCall) {
203+
return $this->resolveMethodReflectionFromStaticCall($callLike);
203204
}
204205

205-
if ($call instanceof New_) {
206-
return $this->resolveMethodReflectionFromNew($call);
206+
if ($callLike instanceof New_) {
207+
return $this->resolveMethodReflectionFromNew($callLike);
207208
}
208209

209-
return $this->resolveFunctionReflectionFromFuncCall($call);
210+
if ($callLike instanceof FuncCall) {
211+
return $this->resolveFunctionReflectionFromFuncCall($callLike);
212+
}
213+
214+
// todo: support NullsafeMethodCall
215+
return null;
210216
}
211217

212218
public function resolveMethodReflectionFromClassMethod(ClassMethod $classMethod, Scope $scope): ?MethodReflection

0 commit comments

Comments
 (0)