Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Sanity test for merging parser
  • Loading branch information
dantleech committed Dec 7, 2025
commit 94066b9f93eedcef42eeda786c4af97ddcb353f7
9 changes: 4 additions & 5 deletions lib/TolerantAstDiff/AstDiff.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
use Microsoft\PhpParser\Token;
use Phpactor\TextDocument\TextEdit;
use Phpactor\TextDocument\TextEdits;
use ReflectionClass;
use ReflectionProperty;
use RuntimeException;
use function Amp\Promise\wait;

final class AstDiff
{
private SourceFileNode $fileSource1;

private SourceFileNode $fileSource2;

public function merge(Node $node1, Node $node2): void
Expand Down Expand Up @@ -182,9 +180,10 @@ private function applyEdit(Node $node, TextEdit $edit): void
private function mapNode(Node $node1, Node $node2): void
{
if ($node2::class !== $node1::class) {
throw new \RuntimeException(sprintf(
throw new RuntimeException(sprintf(
'Can only map nodes of the same type to eachother, got %s and %s',
$node2::class, $node1::class
$node2::class,
$node1::class
));
}

Expand Down
25 changes: 25 additions & 0 deletions lib/TolerantAstDiff/Tests/Unit/AstDiffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,30 @@ public function baz()
}
PHP
];

yield 'intoduce new line' => [
<<<'PHP'
<?php
class Foo
{
public function bar()
{
echo 'foobar';
}
}
PHP,
<<<'PHP'
<?php
class Foo
{
public function bar()
{


echo 'foobar';
}
}
PHP
];
}
}
41 changes: 41 additions & 0 deletions lib/WorseReflection/Bridge/TolerantParser/Parser/MergingParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Phpactor\WorseReflection\Bridge\TolerantParser\Parser;

use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\SourceFileNode;
use Microsoft\PhpParser\Parser;
use Phpactor\TolerantAstDiff\AstDiff;
use Phpactor\WorseReflection\Core\CacheForDocument;

class MergingParser extends Parser
{
/**
* @var array<string,SourceFileNode>
*/
private $documents = [];

public function __construct(
private AstDiff $merger
) {
parent::__construct();
}

public function parseSourceFile(string $source, ?string $uri = null): SourceFileNode
{
if (null === $uri) {
return parent::parseSourceFile($source);
}
if (!isset($this->documents[$uri])) {

@dantleech dantleech Dec 7, 2025 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably, for now, use a Ttl caching mechanism, but should use the #2882

$node = parent::parseSourceFile($source);
$this->documents[$uri] = $node;
return $node;
}

$node1 = $this->documents[$uri];
$node2 = parent::parseSourceFile($source);

$this->merger->merge($node1, $node2);
return $node1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Phpactor\WorseReflection\Tests\Unit\Bridge\TolerantParser\Parser;

use Microsoft\PhpParser\Node\Statement\EchoStatement;
use PHPUnit\Framework\TestCase;
use Phpactor\TolerantAstDiff\AstDiff;
use Phpactor\WorseReflection\Bridge\TolerantParser\Parser\MergingParser;

class MergingParserTest extends TestCase
{
public function testUpdate(): void
{
$parser = new MergingParser(new AstDiff());

$ast = $parser->parseSourceFile(<<<'PHP'
<?php
function a() {
if (true) {
echo 'hello';
echo 'goodbye';
}
if (true) {
echo 'coming';
echo 'going';
}
}
PHP, 'file://path');

$node = $ast->getDescendantNodeAtPosition(46);
self::assertInstanceOf(EchoStatement::class, $node);
$node1OriginalId = spl_object_id($node);
$node = $ast->getDescendantNodeAtPosition(114);
self::assertInstanceOf(EchoStatement::class, $node);
$node2OriginalId = spl_object_id($node);

// new source code introduces new line between the two nodes
$ast = $parser->parseSourceFile(<<<'PHP'
<?php
function a() {
if (true) {
echo 'hello';
echo 'goodbye';
}


if (true) {
echo 'coming';
echo 'going';
}
}
PHP, 'file://path');

$node = $ast->getDescendantNodeAtPosition(46);
self::assertInstanceOf(EchoStatement::class, $node);
self::assertSame($node1OriginalId, spl_object_id($node));
$node = $ast->getDescendantNodeAtPosition(115);
self::assertInstanceOf(EchoStatement::class, $node);
self::assertEquals($node2OriginalId, spl_object_id($node));
}
}