Skip to content

Commit 389abe8

Browse files
committed
RichParser - transform pipes into ordinary calls for custom visitors, and then back
1 parent 68b6599 commit 389abe8

File tree

5 files changed

+123
-64
lines changed

5 files changed

+123
-64
lines changed

src/Parser/ArrowFunctionPipeVisitor.php

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/Parser/ClosurePipeVisitor.php

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Parser;
4+
5+
use Override;
6+
use PhpParser\Node;
7+
use PhpParser\Node\Arg;
8+
use PhpParser\Node\Expr\FuncCall;
9+
use PhpParser\Node\Expr\MethodCall;
10+
use PhpParser\NodeVisitorAbstract;
11+
use function array_merge;
12+
13+
final class PipeTransformerVisitor extends NodeVisitorAbstract
14+
{
15+
16+
public const ORIGINAL_PIPE_ATTRIBUTE_NAME = 'originalPipeAttrs';
17+
18+
#[Override]
19+
public function enterNode(Node $node): ?Node
20+
{
21+
if (!$node instanceof Node\Expr\BinaryOp\Pipe) {
22+
return null;
23+
}
24+
25+
if ($node->right instanceof Node\Expr\FuncCall && $node->right->isFirstClassCallable()) {
26+
return new FuncCall($node->right->name, [
27+
new Arg($node->left),
28+
], attributes: array_merge($node->right->getAttributes(), [
29+
self::ORIGINAL_PIPE_ATTRIBUTE_NAME => $node->getAttributes(),
30+
]));
31+
}
32+
33+
if ($node->right instanceof MethodCall && $node->right->isFirstClassCallable()) {
34+
return new MethodCall($node->right->var, $node->right->name, [
35+
new Arg($node->left),
36+
], attributes: array_merge($node->right->getAttributes(), [
37+
self::ORIGINAL_PIPE_ATTRIBUTE_NAME => $node->getAttributes(),
38+
]));
39+
}
40+
41+
if ($node->right instanceof Node\Expr\StaticCall && $node->right->isFirstClassCallable()) {
42+
return new Node\Expr\StaticCall($node->right->class, $node->right->name, [
43+
new Arg($node->left),
44+
], attributes: array_merge($node->right->getAttributes(), [
45+
self::ORIGINAL_PIPE_ATTRIBUTE_NAME => $node->getAttributes(),
46+
]));
47+
}
48+
49+
return new FuncCall($node->right, [
50+
new Arg($node->left),
51+
], attributes: [
52+
self::ORIGINAL_PIPE_ATTRIBUTE_NAME => $node->getAttributes(),
53+
]);
54+
}
55+
56+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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 function count;
9+
10+
final class ReversePipeTransformerVisitor extends NodeVisitorAbstract
11+
{
12+
13+
#[Override]
14+
public function enterNode(Node $node): ?Node
15+
{
16+
if ($node instanceof Node\Expr\FuncCall && !$node->isFirstClassCallable()) {
17+
$attributes = $node->getAttributes();
18+
$origPipeAttributes = $attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME] ?? [];
19+
if ($origPipeAttributes !== [] && count($node->getArgs()) === 1) {
20+
unset($attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME]);
21+
return new Node\Expr\BinaryOp\Pipe(
22+
$node->getArgs()[0]->value,
23+
new Node\Expr\FuncCall($node->name, [new Node\VariadicPlaceholder()], attributes: $attributes),
24+
attributes: $origPipeAttributes,
25+
);
26+
}
27+
}
28+
29+
if ($node instanceof Node\Expr\MethodCall && !$node->isFirstClassCallable()) {
30+
$attributes = $node->getAttributes();
31+
$origPipeAttributes = $attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME] ?? [];
32+
if ($origPipeAttributes !== [] && count($node->getArgs()) === 1) {
33+
unset($attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME]);
34+
return new Node\Expr\BinaryOp\Pipe(
35+
$node->getArgs()[0]->value,
36+
new Node\Expr\MethodCall($node->var, $node->name, [new Node\VariadicPlaceholder()], attributes: $attributes),
37+
attributes: $origPipeAttributes,
38+
);
39+
}
40+
}
41+
42+
if ($node instanceof Node\Expr\StaticCall && !$node->isFirstClassCallable()) {
43+
$attributes = $node->getAttributes();
44+
$origPipeAttributes = $attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME] ?? [];
45+
if ($origPipeAttributes !== [] && count($node->getArgs()) === 1) {
46+
unset($attributes[PipeTransformerVisitor::ORIGINAL_PIPE_ATTRIBUTE_NAME]);
47+
return new Node\Expr\BinaryOp\Pipe(
48+
$node->getArgs()[0]->value,
49+
new Node\Expr\StaticCall($node->class, $node->name, [new Node\VariadicPlaceholder()], attributes: $attributes),
50+
attributes: $origPipeAttributes,
51+
);
52+
}
53+
}
54+
55+
return null;
56+
}
57+
58+
}

src/Parser/RichParser.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ public function parseString(string $sourceCode): array
7777
throw new ShouldNotHappenException();
7878
}
7979

80+
$pipeTransformer = new NodeTraverser(new PipeTransformerVisitor());
81+
/** @var array<Node\Stmt> */
82+
$nodes = $pipeTransformer->traverse($nodes);
83+
8084
$nodeTraverser = new NodeTraverser();
8185
$nodeTraverser->addVisitor($this->nameResolver);
8286

@@ -89,6 +93,11 @@ public function parseString(string $sourceCode): array
8993

9094
/** @var array<Node\Stmt> */
9195
$nodes = $nodeTraverser->traverse($nodes);
96+
97+
$reversePipeTransformer = new NodeTraverser(new ReversePipeTransformerVisitor());
98+
/** @var array<Node\Stmt> */
99+
$nodes = $reversePipeTransformer->traverse($nodes);
100+
92101
['lines' => $linesToIgnore, 'errors' => $ignoreParseErrors] = $this->getLinesToIgnore($tokens);
93102
if (isset($nodes[0])) {
94103
$nodes[0]->setAttribute('linesToIgnore', $linesToIgnore);

0 commit comments

Comments
 (0)