From 2f035c82bdde15ccfeba3bbc833d3f1dc6ba8151 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 26 Oct 2025 15:14:37 +0700 Subject: [PATCH 1/5] Replace args with VariadicPlaceholder should remove trailing comma --- test/PhpParser/PrettyPrinterTest.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/PhpParser/PrettyPrinterTest.php b/test/PhpParser/PrettyPrinterTest.php index 8e0e472179..3b513704af 100644 --- a/test/PhpParser/PrettyPrinterTest.php +++ b/test/PhpParser/PrettyPrinterTest.php @@ -10,6 +10,7 @@ use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt; +use PhpParser\Node\VariadicPlaceholder; use PhpParser\PrettyPrinter\Standard; class PrettyPrinterTest extends CodeTestAbstract { @@ -305,4 +306,29 @@ public function testInvalidIndent(): void { $this->expectExceptionMessage('Option "indent" must either be all spaces or a single tab'); new PrettyPrinter\Standard(['indent' => "\t "]); } + + public function testTrailingCommaArgToFirstClassCallable(): void { + $parser = (new ParserFactory())->createForNewestSupportedVersion(); + $traverser = new NodeTraverser(new NodeVisitor\CloningVisitor()); + + $oldStmts = $parser->parse(<<<'CODE' + getTokens(); + $newStmts = $traverser->traverse($oldStmts); + + $funcCall = $newStmts[0]->expr; + $funcCall->args = [new VariadicPlaceholder()]; + + $prettyPrinter = new PrettyPrinter\Standard(); + $expected = <<<'CODE' + assertEquals( + $expected, + $prettyPrinter->printFormatPreserving($newStmts, $oldStmts, $oldTokens) + ); + } } From d67576839e5b52c57377e0b877490b7e28465e6d Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 26 Oct 2025 20:15:07 +0700 Subject: [PATCH 2/5] Fix --- lib/PhpParser/Internal/TokenStream.php | 7 ++++++- lib/PhpParser/PrettyPrinterAbstract.php | 8 +++++++- test/PhpParser/PrettyPrinterTest.php | 10 ++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/PhpParser/Internal/TokenStream.php b/lib/PhpParser/Internal/TokenStream.php index cdbe2bdcc9..80bd397895 100644 --- a/lib/PhpParser/Internal/TokenStream.php +++ b/lib/PhpParser/Internal/TokenStream.php @@ -220,12 +220,17 @@ public function getIndentationBefore(int $pos): int { * * @return string Code corresponding to token range, adjusted for indentation */ - public function getTokenCode(int $from, int $to, int $indent): string { + public function getTokenCode(int $from, int $to, int $indent, bool $removeTrailingComma = false): string { $tokens = $this->tokens; $result = ''; for ($pos = $from; $pos < $to; $pos++) { $token = $tokens[$pos]; $id = $token->id; + + if ($removeTrailingComma && $token->text === ',') { + $token->text = ''; + } + $text = $token->text; if ($id === \T_CONSTANT_ENCAPSED_STRING || $id === \T_ENCAPSED_AND_WHITESPACE) { $result .= $text; diff --git a/lib/PhpParser/PrettyPrinterAbstract.php b/lib/PhpParser/PrettyPrinterAbstract.php index 448bc84919..c4badf7ae0 100644 --- a/lib/PhpParser/PrettyPrinterAbstract.php +++ b/lib/PhpParser/PrettyPrinterAbstract.php @@ -10,6 +10,7 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\AssignOp; use PhpParser\Node\Expr\BinaryOp; +use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\Cast; use PhpParser\Node\IntersectionType; use PhpParser\Node\MatchArm; @@ -765,7 +766,12 @@ protected function p( $pos = $subEndPos + 1; } - $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment); + if ($node instanceof CallLike && substr(trim($result), -3) === '...') { + $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment, true); + } else { + $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment); + } + return $result; } diff --git a/test/PhpParser/PrettyPrinterTest.php b/test/PhpParser/PrettyPrinterTest.php index 3b513704af..bcf4b75b79 100644 --- a/test/PhpParser/PrettyPrinterTest.php +++ b/test/PhpParser/PrettyPrinterTest.php @@ -312,7 +312,10 @@ public function testTrailingCommaArgToFirstClassCallable(): void { $traverser = new NodeTraverser(new NodeVisitor\CloningVisitor()); $oldStmts = $parser->parse(<<<'CODE' - getTokens(); @@ -323,7 +326,10 @@ public function testTrailingCommaArgToFirstClassCallable(): void { $prettyPrinter = new PrettyPrinter\Standard(); $expected = <<<'CODE' - assertEquals( From e6f69949c3734d4e372d157cbbbb5fead7b6aa34 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 26 Oct 2025 20:16:34 +0700 Subject: [PATCH 3/5] ensure first class callable --- lib/PhpParser/PrettyPrinterAbstract.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/PhpParser/PrettyPrinterAbstract.php b/lib/PhpParser/PrettyPrinterAbstract.php index c4badf7ae0..4d5d3825e5 100644 --- a/lib/PhpParser/PrettyPrinterAbstract.php +++ b/lib/PhpParser/PrettyPrinterAbstract.php @@ -766,7 +766,7 @@ protected function p( $pos = $subEndPos + 1; } - if ($node instanceof CallLike && substr(trim($result), -3) === '...') { + if ($node instanceof CallLike && substr(trim($result), -3) === '...' && $node->isFirstClassCallable()) { $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment, true); } else { $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment); From 404c19a79668418e6ededd791a3d7ebb14be9b14 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 26 Oct 2025 20:19:40 +0700 Subject: [PATCH 4/5] final touch: do not try to remove trailig comma multiple times --- lib/PhpParser/Internal/TokenStream.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/PhpParser/Internal/TokenStream.php b/lib/PhpParser/Internal/TokenStream.php index 80bd397895..827f363b33 100644 --- a/lib/PhpParser/Internal/TokenStream.php +++ b/lib/PhpParser/Internal/TokenStream.php @@ -229,6 +229,8 @@ public function getTokenCode(int $from, int $to, int $indent, bool $removeTraili if ($removeTrailingComma && $token->text === ',') { $token->text = ''; + // don't try to remove trailing comma multiple times + $removeTrailingComma = false; } $text = $token->text; From 5e60691ef671a0299e1a63e9deffe08ee7ef7403 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 27 Oct 2025 10:28:43 +0700 Subject: [PATCH 5/5] move to test/code/formatPreservation --- test/PhpParser/PrettyPrinterTest.php | 32 ------------------- .../change_args_to_variadic_placeholder.test | 13 ++++++++ 2 files changed, 13 insertions(+), 32 deletions(-) create mode 100644 test/code/formatPreservation/change_args_to_variadic_placeholder.test diff --git a/test/PhpParser/PrettyPrinterTest.php b/test/PhpParser/PrettyPrinterTest.php index bcf4b75b79..8e0e472179 100644 --- a/test/PhpParser/PrettyPrinterTest.php +++ b/test/PhpParser/PrettyPrinterTest.php @@ -10,7 +10,6 @@ use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt; -use PhpParser\Node\VariadicPlaceholder; use PhpParser\PrettyPrinter\Standard; class PrettyPrinterTest extends CodeTestAbstract { @@ -306,35 +305,4 @@ public function testInvalidIndent(): void { $this->expectExceptionMessage('Option "indent" must either be all spaces or a single tab'); new PrettyPrinter\Standard(['indent' => "\t "]); } - - public function testTrailingCommaArgToFirstClassCallable(): void { - $parser = (new ParserFactory())->createForNewestSupportedVersion(); - $traverser = new NodeTraverser(new NodeVisitor\CloningVisitor()); - - $oldStmts = $parser->parse(<<<'CODE' - getTokens(); - $newStmts = $traverser->traverse($oldStmts); - - $funcCall = $newStmts[0]->expr; - $funcCall->args = [new VariadicPlaceholder()]; - - $prettyPrinter = new PrettyPrinter\Standard(); - $expected = <<<'CODE' - assertEquals( - $expected, - $prettyPrinter->printFormatPreserving($newStmts, $oldStmts, $oldTokens) - ); - } } diff --git a/test/code/formatPreservation/change_args_to_variadic_placeholder.test b/test/code/formatPreservation/change_args_to_variadic_placeholder.test new file mode 100644 index 0000000000..962e251df1 --- /dev/null +++ b/test/code/formatPreservation/change_args_to_variadic_placeholder.test @@ -0,0 +1,13 @@ +Replace args with VariadicPlaceholder should remove trailing comma +----- +expr->args = [new Node\VariadicPlaceholder()]; +----- +