1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-16 03:17:02 +01:00
psalm/src/Psalm/Internal/Diff/FileStatementsDiffer.php

177 lines
7.0 KiB
PHP

<?php
namespace Psalm\Internal\Diff;
use function array_merge;
use function end;
use function get_class;
use PhpParser;
use function substr;
/**
* @internal
*/
class FileStatementsDiffer extends AstDiffer
{
/**
* Calculate diff (edit script) from $a to $b.
* @param list<PhpParser\Node\Stmt> $a
* @param list<PhpParser\Node\Stmt> $b
*
* @return array{
* 0: list<string>,
* 1: list<string>,
* 2: list<string>,
* 3: list<array{int, int, int, int}>,
* 4: list<array{int, int}>
* }
*/
public static function diff(array $a, array $b, string $a_code, string $b_code): array
{
[$trace, $x, $y, $bc] = self::calculateTrace(
/**
* @param string $a_code
* @param string $b_code
*
* @return bool
*/
function (
PhpParser\Node\Stmt $a,
PhpParser\Node\Stmt $b,
$a_code,
$b_code,
bool &$body_change = false
): bool {
if (get_class($a) !== get_class($b)) {
return false;
}
if (($a instanceof PhpParser\Node\Stmt\Namespace_ && $b instanceof PhpParser\Node\Stmt\Namespace_)
|| ($a instanceof PhpParser\Node\Stmt\Class_ && $b instanceof PhpParser\Node\Stmt\Class_)
|| ($a instanceof PhpParser\Node\Stmt\Interface_ && $b instanceof PhpParser\Node\Stmt\Interface_)
|| ($a instanceof PhpParser\Node\Stmt\Trait_ && $b instanceof PhpParser\Node\Stmt\Trait_)
) {
return (string)$a->name === (string)$b->name;
}
if (($a instanceof PhpParser\Node\Stmt\Use_
&& $b instanceof PhpParser\Node\Stmt\Use_)
|| ($a instanceof PhpParser\Node\Stmt\GroupUse
&& $b instanceof PhpParser\Node\Stmt\GroupUse)
) {
$a_start = (int)$a->getAttribute('startFilePos');
$a_end = (int)$a->getAttribute('endFilePos');
$b_start = (int)$b->getAttribute('startFilePos');
$b_end = (int)$b->getAttribute('endFilePos');
$a_size = $a_end - $a_start;
$b_size = $b_end - $b_start;
if (substr($a_code, $a_start, $a_size) === substr($b_code, $b_start, $b_size)) {
return true;
}
}
return false;
},
$a,
$b,
$a_code,
$b_code
);
$diff = self::extractDiff($trace, $x, $y, $a, $b, $bc);
$keep = [];
$keep_signature = [];
$add_or_delete = [];
$diff_map = [];
$deletion_ranges = [];
foreach ($diff as $diff_elem) {
if ($diff_elem->type === DiffElem::TYPE_KEEP) {
if ($diff_elem->old instanceof PhpParser\Node\Stmt\Namespace_
&& $diff_elem->new instanceof PhpParser\Node\Stmt\Namespace_
) {
$namespace_keep = NamespaceStatementsDiffer::diff(
(string) $diff_elem->old->name,
$diff_elem->old->stmts,
$diff_elem->new->stmts,
$a_code,
$b_code
);
$keep = array_merge($keep, $namespace_keep[0]);
$keep_signature = array_merge($keep_signature, $namespace_keep[1]);
$add_or_delete = array_merge($add_or_delete, $namespace_keep[2]);
$diff_map = array_merge($diff_map, $namespace_keep[3]);
$deletion_ranges = array_merge($deletion_ranges, $namespace_keep[4]);
} elseif (($diff_elem->old instanceof PhpParser\Node\Stmt\Class_
&& $diff_elem->new instanceof PhpParser\Node\Stmt\Class_)
|| ($diff_elem->old instanceof PhpParser\Node\Stmt\Interface_
&& $diff_elem->new instanceof PhpParser\Node\Stmt\Interface_)
|| ($diff_elem->old instanceof PhpParser\Node\Stmt\Trait_
&& $diff_elem->new instanceof PhpParser\Node\Stmt\Trait_)
) {
$class_keep = ClassStatementsDiffer::diff(
(string) $diff_elem->old->name,
$diff_elem->old->stmts,
$diff_elem->new->stmts,
$a_code,
$b_code
);
$keep = array_merge($keep, $class_keep[0]);
$keep_signature = array_merge($keep_signature, $class_keep[1]);
$add_or_delete = array_merge($add_or_delete, $class_keep[2]);
$diff_map = array_merge($diff_map, $class_keep[3]);
$deletion_ranges = array_merge($deletion_ranges, $class_keep[4]);
}
} elseif ($diff_elem->type === DiffElem::TYPE_REMOVE) {
if ($diff_elem->old instanceof PhpParser\Node\Stmt\Use_
|| $diff_elem->old instanceof PhpParser\Node\Stmt\GroupUse
) {
foreach ($diff_elem->old->uses as $use) {
if ($use->alias) {
$add_or_delete[] = 'use:' . (string) $use->alias;
} else {
$name_parts = $use->name->parts;
$add_or_delete[] = 'use:' . end($name_parts);
}
}
} elseif ($diff_elem->old instanceof PhpParser\Node
&& !$diff_elem->old instanceof PhpParser\Node\Stmt\Namespace_
) {
if ($doc = $diff_elem->old->getDocComment()) {
$start = $doc->getStartFilePos();
} else {
$start = (int)$diff_elem->old->getAttribute('startFilePos');
}
$deletion_ranges[] = [
$start,
(int)$diff_elem->old->getAttribute('endFilePos')
];
}
} elseif ($diff_elem->type === DiffElem::TYPE_ADD) {
if ($diff_elem->new instanceof PhpParser\Node\Stmt\Use_
|| $diff_elem->new instanceof PhpParser\Node\Stmt\GroupUse
) {
foreach ($diff_elem->new->uses as $use) {
if ($use->alias) {
$add_or_delete[] = 'use:' . (string) $use->alias;
} else {
$name_parts = $use->name->parts;
$add_or_delete[] = 'use:' . end($name_parts);
}
}
}
}
}
return [$keep, $keep_signature, $add_or_delete, $diff_map, $deletion_ranges];
}
}