Skip to content

Commit dd2717b

Browse files
committed
Support for pipe operator in PossiblyPure*Collector
1 parent ea53cad commit dd2717b

9 files changed

+148
-14
lines changed

src/Rules/DeadCode/PossiblyPureFuncCallCollector.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,33 @@ public function getNodeType(): string
2727

2828
public function processNode(Node $node, Scope $scope)
2929
{
30-
if (!$node->expr instanceof Node\Expr\FuncCall || $node->expr->isFirstClassCallable()) {
30+
$expr = $node->expr;
31+
if ($expr instanceof Node\Expr\BinaryOp\Pipe) {
32+
if ($expr->right instanceof Node\Expr\FuncCall) {
33+
if (!$expr->right->isFirstClassCallable()) {
34+
return null;
35+
}
36+
37+
$expr = new Node\Expr\FuncCall($expr->right->name, []);
38+
} elseif ($expr->right instanceof Node\Expr\ArrowFunction) {
39+
$expr = $expr->right->expr;
40+
}
41+
}
42+
if (!$expr instanceof Node\Expr\FuncCall) {
43+
return null;
44+
}
45+
if ($expr->isFirstClassCallable()) {
3146
return null;
3247
}
33-
if (!$node->expr->name instanceof Node\Name) {
48+
if (!$expr->name instanceof Node\Name) {
3449
return null;
3550
}
3651

37-
if (!$this->reflectionProvider->hasFunction($node->expr->name, $scope)) {
52+
if (!$this->reflectionProvider->hasFunction($expr->name, $scope)) {
3853
return null;
3954
}
4055

41-
$functionReflection = $this->reflectionProvider->getFunction($node->expr->name, $scope);
56+
$functionReflection = $this->reflectionProvider->getFunction($expr->name, $scope);
4257
if (!$functionReflection->isPure()->maybe()) {
4358
return null;
4459
}

src/Rules/DeadCode/PossiblyPureMethodCallCollector.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,31 @@ public function getNodeType(): string
2626

2727
public function processNode(Node $node, Scope $scope)
2828
{
29-
if (!$node->expr instanceof Node\Expr\MethodCall && !$node->expr instanceof Node\Expr\NullsafeMethodCall) {
29+
$expr = $node->expr;
30+
if ($expr instanceof Node\Expr\BinaryOp\Pipe) {
31+
if ($expr->right instanceof Node\Expr\MethodCall || $expr->right instanceof Node\Expr\NullsafeMethodCall) {
32+
if (!$expr->right->isFirstClassCallable()) {
33+
return null;
34+
}
35+
36+
$expr = new Node\Expr\MethodCall($expr->right->var, $expr->right->name, []);
37+
} elseif ($expr->right instanceof Node\Expr\ArrowFunction) {
38+
$expr = $expr->right->expr;
39+
}
40+
}
41+
42+
if (!$expr instanceof Node\Expr\MethodCall && !$expr instanceof Node\Expr\NullsafeMethodCall) {
3043
return null;
3144
}
32-
if ($node->expr->isFirstClassCallable()) {
45+
if ($expr->isFirstClassCallable()) {
3346
return null;
3447
}
35-
if (!$node->expr->name instanceof Node\Identifier) {
48+
if (!$expr->name instanceof Node\Identifier) {
3649
return null;
3750
}
3851

39-
$methodName = $node->expr->name->toString();
40-
$calledOnType = $scope->getType($node->expr->var);
52+
$methodName = $expr->name->toString();
53+
$calledOnType = $scope->getType($expr->var);
4154
if (!$calledOnType->hasMethod($methodName)->yes()) {
4255
return null;
4356
}

src/Rules/DeadCode/PossiblyPureStaticCallCollector.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,31 @@ public function getNodeType(): string
2626

2727
public function processNode(Node $node, Scope $scope)
2828
{
29-
if (!$node->expr instanceof Node\Expr\StaticCall || $node->expr->isFirstClassCallable()) {
29+
$expr = $node->expr;
30+
if ($expr instanceof Node\Expr\BinaryOp\Pipe) {
31+
if ($expr->right instanceof Node\Expr\StaticCall) {
32+
if (!$expr->right->isFirstClassCallable()) {
33+
return null;
34+
}
35+
36+
$expr = new Node\Expr\StaticCall($expr->right->class, $expr->right->name, []);
37+
} elseif ($expr->right instanceof Node\Expr\ArrowFunction) {
38+
$expr = $expr->right->expr;
39+
}
40+
}
41+
if (!$expr instanceof Node\Expr\StaticCall || $expr->isFirstClassCallable()) {
3042
return null;
3143
}
32-
if (!$node->expr->name instanceof Node\Identifier) {
44+
if (!$expr->name instanceof Node\Identifier) {
3345
return null;
3446
}
3547

36-
if (!$node->expr->class instanceof Node\Name) {
48+
if (!$expr->class instanceof Node\Name) {
3749
return null;
3850
}
3951

40-
$methodName = $node->expr->name->toString();
41-
$calledOnType = $scope->resolveTypeByName($node->expr->class);
52+
$methodName = $expr->name->toString();
53+
$calledOnType = $scope->resolveTypeByName($expr->class);
4254
$methodReflection = $scope->getMethodReflection($calledOnType, $methodName);
4355

4456
if ($methodReflection === null) {

tests/PHPStan/Rules/DeadCode/CallToFunctionStatementWithoutImpurePointsRuleTest.php

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

55
use PHPStan\Rules\Rule;
66
use PHPStan\Testing\RuleTestCase;
7+
use PHPUnit\Framework\Attributes\RequiresPhp;
78

89
/**
910
* @extends RuleTestCase<CallToFunctionStatementWithoutImpurePointsRule>
@@ -26,6 +27,21 @@ public function testRule(): void
2627
]);
2728
}
2829

30+
#[RequiresPhp('>= 8.5')]
31+
public function testPipeOperator(): void
32+
{
33+
$this->analyse([__DIR__ . '/data/call-to-function-without-impure-points-pipe.php'], [
34+
[
35+
'Call to function CallToFunctionWithoutImpurePointsPipe\myFunc() on a separate line has no effect.',
36+
9,
37+
],
38+
[
39+
'Call to function CallToFunctionWithoutImpurePointsPipe\myFunc() on a separate line has no effect.',
40+
10,
41+
],
42+
]);
43+
}
44+
2945
protected function getCollectors(): array
3046
{
3147
return [

tests/PHPStan/Rules/DeadCode/CallToMethodStatementWithoutImpurePointsRuleTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,21 @@ public function testBug12379(): void
9292
$this->analyse([__DIR__ . '/data/bug-12379.php'], []);
9393
}
9494

95+
#[RequiresPhp('>= 8.5')]
96+
public function testPipeOperator(): void
97+
{
98+
$this->analyse([__DIR__ . '/data/call-to-method-without-impure-points-pipe.php'], [
99+
[
100+
'Call to method CallToMethodWithoutImpurePointsPipe\Foo::maybePure() on a separate line has no effect.',
101+
17,
102+
],
103+
[
104+
'Call to method CallToMethodWithoutImpurePointsPipe\Foo::maybePure() on a separate line has no effect.',
105+
18,
106+
],
107+
]);
108+
}
109+
95110
protected function getCollectors(): array
96111
{
97112
return [

tests/PHPStan/Rules/DeadCode/CallToStaticMethodStatementWithoutImpurePointsRuleTest.php

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

55
use PHPStan\Rules\Rule;
66
use PHPStan\Testing\RuleTestCase;
7+
use PHPUnit\Framework\Attributes\RequiresPhp;
78

89
/**
910
* @extends RuleTestCase<CallToStaticMethodStatementWithoutImpurePointsRule>
@@ -58,6 +59,21 @@ public function testRule(): void
5859
]);
5960
}
6061

62+
#[RequiresPhp('>= 8.5')]
63+
public function testPipeOperator(): void
64+
{
65+
$this->analyse([__DIR__ . '/data/call-to-static-method-without-impure-points-pipe.php'], [
66+
[
67+
'Call to CallToStaticMethodWithoutImpurePointsPipe\Foo::doFoo() on a separate line has no effect.',
68+
16,
69+
],
70+
[
71+
'Call to CallToStaticMethodWithoutImpurePointsPipe\Foo::doFoo() on a separate line has no effect.',
72+
17,
73+
],
74+
]);
75+
}
76+
6177
protected function getCollectors(): array
6278
{
6379
return [
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php // lint >= 8.5
2+
3+
namespace CallToFunctionWithoutImpurePointsPipe;
4+
5+
function myFunc()
6+
{
7+
}
8+
9+
5 |> myFunc(...);
10+
5 |> fn ($x) => myFunc($x);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php // lint >= 8.5
2+
3+
namespace CallToMethodWithoutImpurePointsPipe;
4+
5+
class Foo
6+
{
7+
8+
public function maybePure(int $o): int
9+
{
10+
return 5;
11+
}
12+
13+
}
14+
15+
function (): void {
16+
$foo = new Foo();
17+
5 |> $foo->maybePure(...);
18+
5 |> fn ($x) => $foo->maybePure($x);
19+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace CallToStaticMethodWithoutImpurePointsPipe;
4+
5+
class Foo
6+
{
7+
8+
public static function doFoo(int $o): int
9+
{
10+
return 1;
11+
}
12+
13+
}
14+
15+
function (): void {
16+
5 |> Foo::doFoo(...);
17+
5 |> fn ($x) => Foo::doFoo($x);
18+
};

0 commit comments

Comments
 (0)