Skip to content

Commit d0ae37e

Browse files
committed
Fix special implode behaviour in pipe operator
1 parent 26b0eb7 commit d0ae37e

File tree

7 files changed

+34
-8
lines changed

7 files changed

+34
-8
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
use PHPStan\Parser\ImmediatelyInvokedClosureVisitor;
133133
use PHPStan\Parser\LineAttributesVisitor;
134134
use PHPStan\Parser\Parser;
135+
use PHPStan\Parser\ReversePipeTransformerVisitor;
135136
use PHPStan\Php\PhpVersion;
136137
use PHPStan\PhpDoc\PhpDocInheritanceResolver;
137138
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
@@ -3385,7 +3386,7 @@ static function (): void {
33853386
} elseif ($expr instanceof BinaryOp\Pipe) {
33863387
if ($expr->right instanceof FuncCall && $expr->right->isFirstClassCallable()) {
33873388
$exprResult = $this->processExprNode($stmt, new FuncCall($expr->right->name, [
3388-
new Arg($expr->left, attributes: $expr->left->getAttributes()),
3389+
new Arg($expr->left, attributes: $expr->getAttribute(ReversePipeTransformerVisitor::ARG_ATTRIBUTES_NAME, [])),
33893390
], array_merge($expr->right->getAttributes(), ['virtualPipeOperatorCall' => true])), $scope, $nodeCallback, $context);
33903391
$scope = $exprResult->getScope();
33913392
$hasYield = $exprResult->hasYield();
@@ -3394,7 +3395,7 @@ static function (): void {
33943395
$isAlwaysTerminating = $exprResult->isAlwaysTerminating();
33953396
} elseif ($expr->right instanceof MethodCall && $expr->right->isFirstClassCallable()) {
33963397
$exprResult = $this->processExprNode($stmt, new MethodCall($expr->right->var, $expr->right->name, [
3397-
new Arg($expr->left, attributes: $expr->left->getAttributes()),
3398+
new Arg($expr->left, attributes: $expr->getAttribute(ReversePipeTransformerVisitor::ARG_ATTRIBUTES_NAME, [])),
33983399
], array_merge($expr->right->getAttributes(), ['virtualPipeOperatorCall' => true])), $scope, $nodeCallback, $context);
33993400
$scope = $exprResult->getScope();
34003401
$hasYield = $exprResult->hasYield();
@@ -3403,7 +3404,7 @@ static function (): void {
34033404
$isAlwaysTerminating = $exprResult->isAlwaysTerminating();
34043405
} elseif ($expr->right instanceof StaticCall && $expr->right->isFirstClassCallable()) {
34053406
$exprResult = $this->processExprNode($stmt, new StaticCall($expr->right->class, $expr->right->name, [
3406-
new Arg($expr->left, attributes: $expr->left->getAttributes()),
3407+
new Arg($expr->left, attributes: $expr->getAttribute(ReversePipeTransformerVisitor::ARG_ATTRIBUTES_NAME, [])),
34073408
], array_merge($expr->right->getAttributes(), ['virtualPipeOperatorCall' => true])), $scope, $nodeCallback, $context);
34083409
$scope = $exprResult->getScope();
34093410
$hasYield = $exprResult->hasYield();
@@ -3412,7 +3413,7 @@ static function (): void {
34123413
$isAlwaysTerminating = $exprResult->isAlwaysTerminating();
34133414
} else {
34143415
$exprResult = $this->processExprNode($stmt, new FuncCall($expr->right, [
3415-
new Arg($expr->left, attributes: $expr->left->getAttributes()),
3416+
new Arg($expr->left, attributes: $expr->getAttribute(ReversePipeTransformerVisitor::ARG_ATTRIBUTES_NAME, [])),
34163417
], array_merge($expr->right->getAttributes(), ['virtualPipeOperatorCall' => true])), $scope, $nodeCallback, $context);
34173418
$scope = $exprResult->getScope();
34183419
$hasYield = $exprResult->hasYield();

src/Parser/CurlSetOptArgVisitor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public function enterNode(Node $node): ?Node
1919
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name && !$node->isFirstClassCallable()) {
2020
$functionName = $node->name->toLowerString();
2121
if ($functionName === 'curl_setopt') {
22-
$args = $node->getRawArgs();
22+
$args = $node->getArgs();
2323
if (isset($args[0])) {
2424
$args[0]->setAttribute(self::ATTRIBUTE_NAME, true);
2525
}

src/Parser/CurlSetOptArrayArgVisitor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ final class CurlSetOptArrayArgVisitor extends NodeVisitorAbstract
1616
#[Override]
1717
public function enterNode(Node $node): ?Node
1818
{
19-
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name) {
19+
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name && !$node->isFirstClassCallable()) {
2020
$functionName = $node->name->toLowerString();
2121
if ($functionName === 'curl_setopt_array') {
22-
$args = $node->getRawArgs();
22+
$args = $node->getArgs();
2323
if (isset($args[1])) {
2424
$args[1]->setAttribute(self::ATTRIBUTE_NAME, true);
2525
}

src/Parser/ImplodeArgVisitor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function enterNode(Node $node): ?Node
2020
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name && !$node->isFirstClassCallable()) {
2121
$functionName = $node->name->toLowerString();
2222
if (in_array($functionName, ['implode', 'join'], true)) {
23-
$args = $node->getRawArgs();
23+
$args = $node->getArgs();
2424
if (isset($args[0])) {
2525
$args[0]->setAttribute(self::ATTRIBUTE_NAME, true);
2626
}

src/Parser/ReversePipeTransformerVisitor.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
final class ReversePipeTransformerVisitor extends NodeVisitorAbstract
1111
{
1212

13+
public const ARG_ATTRIBUTES_NAME = 'argAttributes';
14+
1315
#[Override]
1416
public function enterNode(Node $node): ?Node
1517
{
1618
if ($node instanceof Node\Expr\FuncCall && !$node->isFirstClassCallable()) {
1719
$attributes = $node->getAttributes();
1820
$origPipeAttributes = $attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME] ?? [];
1921
if ($origPipeAttributes !== [] && count($node->getArgs()) === 1) {
22+
$origPipeAttributes[self::ARG_ATTRIBUTES_NAME] = $node->getArgs()[0]->getAttributes();
2023
if ($node->name instanceof Node\Name) {
2124
unset($attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME]);
2225
return new Node\Expr\BinaryOp\Pipe(
@@ -38,6 +41,7 @@ public function enterNode(Node $node): ?Node
3841
$attributes = $node->getAttributes();
3942
$origPipeAttributes = $attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME] ?? [];
4043
if ($origPipeAttributes !== [] && count($node->getArgs()) === 1) {
44+
$origPipeAttributes[self::ARG_ATTRIBUTES_NAME] = $node->getArgs()[0]->getAttributes();
4145
unset($attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME]);
4246
return new Node\Expr\BinaryOp\Pipe(
4347
$node->getArgs()[0]->value,
@@ -51,6 +55,7 @@ public function enterNode(Node $node): ?Node
5155
$attributes = $node->getAttributes();
5256
$origPipeAttributes = $attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME] ?? [];
5357
if ($origPipeAttributes !== [] && count($node->getArgs()) === 1) {
58+
$origPipeAttributes[self::ARG_ATTRIBUTES_NAME] = $node->getArgs()[0]->getAttributes();
5459
unset($attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME]);
5560
return new Node\Expr\BinaryOp\Pipe(
5661
$node->getArgs()[0]->value,

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2431,6 +2431,14 @@ public function testPipeOperator(): void
24312431
'Result of function FuncCallPipe\doBar (void) is used.',
24322432
28,
24332433
],
2434+
[
2435+
'Parameter #1 $separator of function implode expects array, string given.',
2436+
38,
2437+
],
2438+
[
2439+
'Parameter #1 $separator of function implode expects array, string given.',
2440+
40,
2441+
],
24342442
]);
24352443
}
24362444

tests/PHPStan/Rules/Functions/data/func-call-pipe.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,15 @@ public function doFoo(): void
2929
}
3030

3131
}
32+
33+
class TestImplode
34+
{
35+
36+
public function doFoo(): void
37+
{
38+
implode('foo');
39+
40+
'foo' |> implode(...);
41+
}
42+
43+
}

0 commit comments

Comments
 (0)