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

145 lines
5.3 KiB
PHP
Raw Normal View History

<?php
namespace Psalm\Diff;
use PhpParser;
/**
* @internal
*/
class ClassStatementsDiffer extends Differ
{
/**
* Calculate diff (edit script) from $a to $b.
*
* @param PhpParser\Node\Stmt[] $a
* @param PhpParser\Node\Stmt[] $b New array
*
* @return array{0:array<int, string>, 1:array<int, string>, 2: array<int, array{0: int, 1: int, 2: int, 3: int}>}
*/
public static function diff(string $name, array $a, array $b, string $a_code, string $b_code)
{
$diff_map = [];
list($trace, $x, $y, $bc) = self::calculateTrace(
function (
PhpParser\Node\Stmt $a,
PhpParser\Node\Stmt $b,
string $a_code,
string $b_code,
bool &$body_change = false
) use (&$diff_map) : bool {
if (get_class($a) !== get_class($b)) {
return false;
}
$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_comments_end = $a_start;
$b_comments_end = $b_start;
$a_comments = $a->getComments();
$b_comments = $b->getComments();
$signature_change = false;
$body_change = false;
if ($a_comments) {
if (!$b_comments) {
$signature_change = true;
}
$a_start = $a_comments[0]->getFilePos();
}
if ($b_comments) {
if (!$a_comments) {
$signature_change = true;
}
$b_start = $b_comments[0]->getFilePos();
}
$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)) {
$diff_map[] = [$a_start, $a_end, $b_start - $a_start, $b->getLine() - $a->getLine()];
return true;
}
if (!$signature_change
&& substr($a_code, $a_start, $a_comments_end - $a_start)
!== substr($b_code, $b_start, $b_comments_end - $b_start)
) {
$signature_change = true;
}
if ($a instanceof PhpParser\Node\Stmt\ClassMethod && $b instanceof PhpParser\Node\Stmt\ClassMethod) {
if ((string) $a->name !== (string) $b->name) {
return false;
}
$a_stmts_start = $a->stmts ? (int) $a->stmts[0]->getAttribute('startFilePos') : $a_end;
$b_stmts_start = $b->stmts ? (int) $b->stmts[0]->getAttribute('startFilePos') : $b_end;
$body_change = substr($a_code, $a_stmts_start, $a_end - $a_stmts_start)
!== substr($b_code, $b_stmts_start, $b_end - $b_stmts_start);
$signature_change = $signature_change
|| substr($a_code, $a_start, $a_stmts_start - $a_start)
!== substr($b_code, $b_start, $b_stmts_start - $b_start);
} else {
$signature_change = true;
}
if (!$signature_change && !$body_change) {
$diff_map[] = [$a_start, $a_end, $b_start - $a_start, $b->getLine() - $a->getLine()];
}
return !$signature_change;
},
$a,
$b,
$a_code,
$b_code
);
$diff = self::extractDiff($trace, $x, $y, $a, $b, $bc);
$keep = [];
$keep_signature = [];
foreach ($diff as $diff_elem) {
if ($diff_elem->type === DiffElem::TYPE_KEEP) {
if ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassMethod) {
$keep[] = strtolower($name) . '::' . strtolower((string) $diff_elem->old->name);
} elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\Property) {
foreach ($diff_elem->old->props as $prop) {
$keep[] = strtolower($name) . '::$' . $prop->name;
}
} elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassConst) {
foreach ($diff_elem->old->consts as $const) {
$keep[] = strtolower($name) . '::' . $const->name;
}
}
} elseif ($diff_elem->type === DiffElem::TYPE_KEEP_SIGNATURE) {
if ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassMethod) {
$keep_signature[] = strtolower($name) . '::' . strtolower((string) $diff_elem->old->name);
} elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\Property) {
foreach ($diff_elem->old->props as $prop) {
$keep_signature[] = strtolower($name) . '::$' . $prop->name;
}
}
}
};
return [$keep, $keep_signature, $diff_map];
}
}