2018-09-25 23:52:27 +02:00
|
|
|
<?php
|
2018-11-06 03:57:36 +01:00
|
|
|
namespace Psalm\Internal\Diff;
|
2018-09-25 23:52:27 +02:00
|
|
|
|
2019-06-26 22:52:29 +02:00
|
|
|
use function array_merge;
|
|
|
|
use function end;
|
2019-07-05 22:24:00 +02:00
|
|
|
use function get_class;
|
|
|
|
use PhpParser;
|
|
|
|
use function substr;
|
2018-09-25 23:52:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2018-10-26 06:59:14 +02:00
|
|
|
class FileStatementsDiffer extends AstDiffer
|
2018-09-25 23:52:27 +02:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Calculate diff (edit script) from $a to $b.
|
2020-10-17 18:36:44 +02:00
|
|
|
* @param list<PhpParser\Node\Stmt> $a
|
|
|
|
* @param list<PhpParser\Node\Stmt> $b
|
2018-09-25 23:52:27 +02:00
|
|
|
*
|
2018-09-26 23:54:08 +02:00
|
|
|
* @return array{
|
2020-10-17 18:36:44 +02:00
|
|
|
* 0: list<string>,
|
|
|
|
* 1: list<string>,
|
|
|
|
* 2: list<string>,
|
|
|
|
* 3: list<array{0: int, 1: int, 2: int, 3: int}>
|
2018-09-26 23:54:08 +02:00
|
|
|
* }
|
2018-09-25 23:52:27 +02:00
|
|
|
*/
|
2020-09-07 01:36:47 +02:00
|
|
|
public static function diff(array $a, array $b, string $a_code, string $b_code): array
|
2018-09-25 23:52:27 +02:00
|
|
|
{
|
2020-09-02 06:17:41 +02:00
|
|
|
[$trace, $x, $y, $bc] = self::calculateTrace(
|
2018-09-26 00:37:24 +02:00
|
|
|
/**
|
2018-09-27 19:32:08 +02:00
|
|
|
* @param string $a_code
|
|
|
|
* @param string $b_code
|
|
|
|
*
|
|
|
|
* @return bool
|
2018-09-26 00:37:24 +02:00
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
function (
|
|
|
|
PhpParser\Node\Stmt $a,
|
|
|
|
PhpParser\Node\Stmt $b,
|
|
|
|
$a_code,
|
|
|
|
$b_code,
|
|
|
|
bool &$body_change = false
|
|
|
|
): bool {
|
2018-09-25 23:52:27 +02:00
|
|
|
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_)
|
2018-10-03 19:58:32 +02:00
|
|
|
|| ($a instanceof PhpParser\Node\Stmt\Trait_ && $b instanceof PhpParser\Node\Stmt\Trait_)
|
2018-09-25 23:52:27 +02:00
|
|
|
) {
|
|
|
|
return (string)$a->name === (string)$b->name;
|
|
|
|
}
|
|
|
|
|
2018-10-31 01:51:52 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-25 23:52:27 +02:00
|
|
|
return false;
|
|
|
|
},
|
|
|
|
$a,
|
|
|
|
$b,
|
|
|
|
$a_code,
|
|
|
|
$b_code
|
|
|
|
);
|
|
|
|
|
|
|
|
$diff = self::extractDiff($trace, $x, $y, $a, $b, $bc);
|
|
|
|
|
|
|
|
$keep = [];
|
|
|
|
$keep_signature = [];
|
2018-10-03 19:58:32 +02:00
|
|
|
$add_or_delete = [];
|
2018-09-26 22:33:59 +02:00
|
|
|
$diff_map = [];
|
2018-09-25 23:52:27 +02:00
|
|
|
|
|
|
|
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]);
|
2018-10-03 19:58:32 +02:00
|
|
|
$add_or_delete = array_merge($add_or_delete, $namespace_keep[2]);
|
2018-09-26 23:54:08 +02:00
|
|
|
$diff_map = array_merge($diff_map, $namespace_keep[3]);
|
2018-09-25 23:52:27 +02:00
|
|
|
} 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_)
|
2018-10-03 19:58:32 +02:00
|
|
|
|| ($diff_elem->old instanceof PhpParser\Node\Stmt\Trait_
|
|
|
|
&& $diff_elem->new instanceof PhpParser\Node\Stmt\Trait_)
|
2018-09-25 23:52:27 +02:00
|
|
|
) {
|
|
|
|
$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]);
|
2018-10-03 19:58:32 +02:00
|
|
|
$add_or_delete = array_merge($add_or_delete, $class_keep[2]);
|
2018-09-26 23:54:08 +02:00
|
|
|
$diff_map = array_merge($diff_map, $class_keep[3]);
|
2018-09-25 23:52:27 +02:00
|
|
|
}
|
2018-10-27 05:04:38 +02:00
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-02 02:52:39 +01:00
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-25 23:52:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 19:58:32 +02:00
|
|
|
return [$keep, $keep_signature, $add_or_delete, $diff_map];
|
2018-09-25 23:52:27 +02:00
|
|
|
}
|
|
|
|
}
|