Skip to content

Commit cbae9b7

Browse files
committed
Infer closure parameter types in pipe operator
1 parent e9df52e commit cbae9b7

File tree

3 files changed

+99
-2
lines changed

3 files changed

+99
-2
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Parser;
4+
5+
use Override;
6+
use PhpParser\Node;
7+
use PhpParser\NodeVisitorAbstract;
8+
use PHPStan\DependencyInjection\AutowiredService;
9+
10+
#[AutowiredService]
11+
final class ArrowFunctionPipeVisitor extends NodeVisitorAbstract
12+
{
13+
14+
#[Override]
15+
public function enterNode(Node $node): ?Node
16+
{
17+
if (!$node instanceof Node\Expr\BinaryOp\Pipe) {
18+
return null;
19+
}
20+
21+
if (!$node->right instanceof Node\Expr\ArrowFunction) {
22+
return null;
23+
}
24+
25+
$node->right->setAttribute(ArrowFunctionArgVisitor::ATTRIBUTE_NAME, [
26+
new Node\Arg($node->left),
27+
]);
28+
29+
return null;
30+
}
31+
32+
}

src/Parser/ClosurePipeVisitor.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Parser;
4+
5+
use Override;
6+
use PhpParser\Node;
7+
use PhpParser\NodeVisitorAbstract;
8+
use PHPStan\DependencyInjection\AutowiredService;
9+
10+
#[AutowiredService]
11+
final class ClosurePipeVisitor extends NodeVisitorAbstract
12+
{
13+
14+
#[Override]
15+
public function enterNode(Node $node): ?Node
16+
{
17+
if (!$node instanceof Node\Expr\BinaryOp\Pipe) {
18+
return null;
19+
}
20+
21+
if (!$node->right instanceof Node\Expr\Closure) {
22+
return null;
23+
}
24+
25+
$node->right->setAttribute(ClosureArgVisitor::ATTRIBUTE_NAME, [
26+
new Node\Arg($node->left),
27+
]);
28+
29+
return null;
30+
}
31+
32+
}

tests/PHPStan/Analyser/nsrt/pipe-operator.php

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public function doBar(): void
3434

3535
}
3636

37+
/**
38+
* @return positive-int
39+
*/
3740
function doFoo(): int
3841
{
3942

@@ -44,8 +47,38 @@ class FunctionFoo
4447

4548
public function doBar(): void
4649
{
47-
assertType('int', 'foo' |> doFoo(...));
48-
assertType('int', 'foo' |> 'PipeOperatorTypes\\doFoo');
50+
assertType('int<1, max>', 'foo' |> doFoo(...));
51+
assertType('int<1, max>', 'foo' |> 'PipeOperatorTypes\\doFoo');
52+
}
53+
54+
public function doBaz(): void
55+
{
56+
doFoo() |> function ($x) {
57+
assertType('int<1, max>', $x);
58+
};
59+
60+
doFoo() |> fn ($x) => assertType('int<1, max>', $x);
61+
62+
doFoo() |> function (int $x) {
63+
assertType('int<1, max>', $x);
64+
};
65+
66+
doFoo() |> fn (int $x) => assertType('int<1, max>', $x);
67+
}
68+
69+
public function doBaz2(): void
70+
{
71+
(function ($x) {
72+
assertType('int<1, max>', $x);
73+
})(doFoo());
74+
75+
(fn ($x) => assertType('int<1, max>', $x))(doFoo());
76+
77+
(function (int $x) {
78+
assertType('int<1, max>', $x);
79+
})(doFoo());
80+
81+
(fn (int $x) => assertType('int<1, max>', $x))(doFoo());
4982
}
5083

5184
}

0 commit comments

Comments
 (0)