mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Fix #5297 - be more sensitive to additions and deletions in language server mode
This commit is contained in:
parent
7db742dee3
commit
ad82c93edb
@ -576,11 +576,6 @@ class Analyzer
|
||||
$i = 0;
|
||||
|
||||
foreach ($this->files_to_analyze as $file_path => $_) {
|
||||
// Remove all current maps for the file, so new analysis doesn't
|
||||
// only append to existing data.
|
||||
unset($this->reference_map[$file_path]);
|
||||
unset($this->type_map[$file_path]);
|
||||
unset($this->argument_map[$file_path]);
|
||||
$analysis_worker($i, $file_path);
|
||||
++$i;
|
||||
|
||||
@ -614,6 +609,7 @@ class Analyzer
|
||||
$errored_files = $statements_provider->getErrors();
|
||||
|
||||
$diff_map = $statements_provider->getDiffMap();
|
||||
$deletion_ranges = $statements_provider->getDeletionRanges();
|
||||
|
||||
$method_references_to_class_members
|
||||
= $file_reference_provider->getAllMethodReferencesToClassMembers();
|
||||
@ -774,7 +770,7 @@ class Analyzer
|
||||
}
|
||||
}
|
||||
|
||||
$this->shiftFileOffsets($diff_map);
|
||||
$this->shiftFileOffsets($diff_map, $deletion_ranges);
|
||||
|
||||
foreach ($this->files_to_analyze as $file_path) {
|
||||
$file_reference_provider->clearExistingIssuesForFile($file_path);
|
||||
@ -876,9 +872,9 @@ class Analyzer
|
||||
|
||||
/**
|
||||
* @param array<string, array<int, array{int, int, int, int}>> $diff_map
|
||||
*
|
||||
* @param array<string, array<int, array{int, int}>> $deletion_ranges
|
||||
*/
|
||||
public function shiftFileOffsets(array $diff_map): void
|
||||
public function shiftFileOffsets(array $diff_map, array $deletion_ranges): void
|
||||
{
|
||||
foreach ($this->existing_issues as $file_path => &$file_issues) {
|
||||
if (!isset($this->analyzed_methods[$file_path])) {
|
||||
@ -886,39 +882,36 @@ class Analyzer
|
||||
}
|
||||
|
||||
$file_diff_map = $diff_map[$file_path] ?? [];
|
||||
$file_deletion_ranges = $deletion_ranges[$file_path] ?? [];
|
||||
|
||||
if (!$file_diff_map) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$first_diff_offset = $file_diff_map[0][0];
|
||||
$last_diff_offset = $file_diff_map[count($file_diff_map) - 1][1];
|
||||
|
||||
foreach ($file_issues as $i => &$issue_data) {
|
||||
if ($issue_data->to < $first_diff_offset || $issue_data->from > $last_diff_offset) {
|
||||
unset($file_issues[$i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$matched = false;
|
||||
|
||||
foreach ($file_diff_map as [$from, $to, $file_offset, $line_offset]) {
|
||||
if ($issue_data->from >= $from
|
||||
&& $issue_data->from <= $to
|
||||
&& !$matched
|
||||
) {
|
||||
$issue_data->from += $file_offset;
|
||||
$issue_data->to += $file_offset;
|
||||
$issue_data->snippet_from += $file_offset;
|
||||
$issue_data->snippet_to += $file_offset;
|
||||
$issue_data->line_from += $line_offset;
|
||||
$issue_data->line_to += $line_offset;
|
||||
$matched = true;
|
||||
if ($file_deletion_ranges) {
|
||||
foreach ($file_issues as $i => &$issue_data) {
|
||||
foreach ($file_deletion_ranges as [$from, $to]) {
|
||||
if ($issue_data->from >= $from
|
||||
&& $issue_data->from <= $to
|
||||
) {
|
||||
unset($file_issues[$i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$matched) {
|
||||
unset($file_issues[$i]);
|
||||
if ($file_diff_map) {
|
||||
foreach ($file_issues as $issue_data) {
|
||||
foreach ($file_diff_map as [$from, $to, $file_offset, $line_offset]) {
|
||||
if ($issue_data->from >= $from
|
||||
&& $issue_data->from <= $to
|
||||
) {
|
||||
$issue_data->from += $file_offset;
|
||||
$issue_data->to += $file_offset;
|
||||
$issue_data->snippet_from += $file_offset;
|
||||
$issue_data->snippet_to += $file_offset;
|
||||
$issue_data->line_from += $line_offset;
|
||||
$issue_data->line_to += $line_offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -930,26 +923,29 @@ class Analyzer
|
||||
}
|
||||
|
||||
$file_diff_map = $diff_map[$file_path] ?? [];
|
||||
$file_deletion_ranges = $deletion_ranges[$file_path] ?? [];
|
||||
|
||||
if (!$file_diff_map) {
|
||||
continue;
|
||||
if ($file_deletion_ranges) {
|
||||
foreach ($reference_map as $reference_from => $_) {
|
||||
foreach ($file_deletion_ranges as [$from, $to]) {
|
||||
if ($reference_from >= $from && $reference_from <= $to) {
|
||||
unset($reference_map[$reference_from]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$first_diff_offset = $file_diff_map[0][0];
|
||||
$last_diff_offset = $file_diff_map[count($file_diff_map) - 1][1];
|
||||
|
||||
foreach ($reference_map as $reference_from => [$reference_to, $tag]) {
|
||||
if ($reference_to < $first_diff_offset || $reference_from > $last_diff_offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($file_diff_map as [$from, $to, $file_offset]) {
|
||||
if ($reference_from >= $from && $reference_from <= $to) {
|
||||
unset($reference_map[$reference_from]);
|
||||
$reference_map[$reference_from += $file_offset] = [
|
||||
$reference_to += $file_offset,
|
||||
$tag,
|
||||
];
|
||||
if ($file_diff_map) {
|
||||
foreach ($reference_map as $reference_from => [$reference_to, $tag]) {
|
||||
foreach ($file_diff_map as [$from, $to, $file_offset]) {
|
||||
if ($reference_from >= $from && $reference_from <= $to) {
|
||||
unset($reference_map[$reference_from]);
|
||||
$reference_map[$reference_from += $file_offset] = [
|
||||
$reference_to += $file_offset,
|
||||
$tag,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -962,26 +958,29 @@ class Analyzer
|
||||
}
|
||||
|
||||
$file_diff_map = $diff_map[$file_path] ?? [];
|
||||
$file_deletion_ranges = $deletion_ranges[$file_path] ?? [];
|
||||
|
||||
if (!$file_diff_map) {
|
||||
continue;
|
||||
if ($file_deletion_ranges) {
|
||||
foreach ($type_map as $type_from => $_) {
|
||||
foreach ($file_deletion_ranges as [$from, $to]) {
|
||||
if ($type_from >= $from && $type_from <= $to) {
|
||||
unset($type_map[$type_from]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$first_diff_offset = $file_diff_map[0][0];
|
||||
$last_diff_offset = $file_diff_map[count($file_diff_map) - 1][1];
|
||||
|
||||
foreach ($type_map as $type_from => [$type_to, $tag]) {
|
||||
if ($type_to < $first_diff_offset || $type_from > $last_diff_offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($file_diff_map as [$from, $to, $file_offset]) {
|
||||
if ($type_from >= $from && $type_from <= $to) {
|
||||
unset($type_map[$type_from]);
|
||||
$type_map[$type_from += $file_offset] = [
|
||||
$type_to += $file_offset,
|
||||
$tag,
|
||||
];
|
||||
if ($file_diff_map) {
|
||||
foreach ($type_map as $type_from => [$type_to, $tag]) {
|
||||
foreach ($file_diff_map as [$from, $to, $file_offset]) {
|
||||
if ($type_from >= $from && $type_from <= $to) {
|
||||
unset($type_map[$type_from]);
|
||||
$type_map[$type_from += $file_offset] = [
|
||||
$type_to += $file_offset,
|
||||
$tag,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -994,27 +993,30 @@ class Analyzer
|
||||
}
|
||||
|
||||
$file_diff_map = $diff_map[$file_path] ?? [];
|
||||
$file_deletion_ranges = $deletion_ranges[$file_path] ?? [];
|
||||
|
||||
if (!$file_diff_map) {
|
||||
continue;
|
||||
if ($file_deletion_ranges) {
|
||||
foreach ($argument_map as $argument_from => $_) {
|
||||
foreach ($file_deletion_ranges as [$from, $to]) {
|
||||
if ($argument_from >= $from && $argument_from <= $to) {
|
||||
unset($argument_map[$argument_from]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$first_diff_offset = $file_diff_map[0][0];
|
||||
$last_diff_offset = $file_diff_map[count($file_diff_map) - 1][1];
|
||||
|
||||
foreach ($argument_map as $argument_from => [$argument_to, $method_id, $argument_number]) {
|
||||
if ($argument_to < $first_diff_offset || $argument_from > $last_diff_offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($file_diff_map as [$from, $to, $file_offset]) {
|
||||
if ($argument_from >= $from && $argument_from <= $to) {
|
||||
unset($argument_map[$argument_from]);
|
||||
$argument_map[$argument_from += $file_offset] = [
|
||||
$argument_to += $file_offset,
|
||||
$method_id,
|
||||
$argument_number,
|
||||
];
|
||||
if ($file_diff_map) {
|
||||
foreach ($argument_map as $argument_from => [$argument_to, $method_id, $argument_number]) {
|
||||
foreach ($file_diff_map as [$from, $to, $file_offset]) {
|
||||
if ($argument_from >= $from && $argument_from <= $to) {
|
||||
unset($argument_map[$argument_from]);
|
||||
$argument_map[$argument_from += $file_offset] = [
|
||||
$argument_to += $file_offset,
|
||||
$method_id,
|
||||
$argument_number,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,8 @@ use function substr;
|
||||
* issues:array<string, list<IssueData>>,
|
||||
* changed_members:array<string, array<string, bool>>,
|
||||
* unchanged_signature_members:array<string, array<string, bool>>,
|
||||
* diff_map:array<string, array<int, array{0:int, 1:int, 2:int, 3:int}>>,
|
||||
* diff_map:array<string, array<int, array{int, int, int, int}>>,
|
||||
* deletion_ranges:array<string, array<int, array{int, int}>>,
|
||||
* errors:array<string, bool>,
|
||||
* classlike_storage:array<string, \Psalm\Storage\ClassLikeStorage>,
|
||||
* file_storage:array<lowercase-string, \Psalm\Storage\FileStorage>,
|
||||
@ -381,6 +382,7 @@ class Scanner
|
||||
'changed_members' => $statements_provider->getChangedMembers(),
|
||||
'unchanged_signature_members' => $statements_provider->getUnchangedSignatureMembers(),
|
||||
'diff_map' => $statements_provider->getDiffMap(),
|
||||
'deletion_ranges' => $statements_provider->getDeletionRanges(),
|
||||
'errors' => $statements_provider->getErrors(),
|
||||
'classlike_storage' => $codebase->classlike_storage_provider->getAll(),
|
||||
'file_storage' => $codebase->file_storage_provider->getAll(),
|
||||
@ -410,6 +412,9 @@ class Scanner
|
||||
$this->codebase->statements_provider->addDiffMap(
|
||||
$pool_data['diff_map']
|
||||
);
|
||||
$this->codebase->statements_provider->addDeletionRanges(
|
||||
$pool_data['deletion_ranges']
|
||||
);
|
||||
$this->codebase->statements_provider->addErrors($pool_data['errors']);
|
||||
|
||||
if ($this->codebase->taint_flow_graph && $pool_data['taint_data']) {
|
||||
|
@ -24,7 +24,8 @@ class ClassStatementsDiffer extends AstDiffer
|
||||
* 0: list<string>,
|
||||
* 1: list<string>,
|
||||
* 2: list<string>,
|
||||
* 3: array<int, array{0: int, 1: int, 2: int, 3: int}>
|
||||
* 3: array<int, array{int, int, int, int}>,
|
||||
* 4: list<array{int, int}>
|
||||
* }
|
||||
*/
|
||||
public static function diff(string $name, array $a, array $b, string $a_code, string $b_code): array
|
||||
@ -187,6 +188,7 @@ class ClassStatementsDiffer extends AstDiffer
|
||||
$keep = [];
|
||||
$keep_signature = [];
|
||||
$add_or_delete = [];
|
||||
$deletion_ranges = [];
|
||||
|
||||
foreach ($diff as $diff_elem) {
|
||||
if ($diff_elem->type === DiffElem::TYPE_KEEP) {
|
||||
@ -214,7 +216,7 @@ class ClassStatementsDiffer extends AstDiffer
|
||||
}
|
||||
}
|
||||
} elseif ($diff_elem->type === DiffElem::TYPE_REMOVE || $diff_elem->type === DiffElem::TYPE_ADD) {
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
/** @var PhpParser\Node */
|
||||
$affected_elem = $diff_elem->type === DiffElem::TYPE_REMOVE ? $diff_elem->old : $diff_elem->new;
|
||||
if ($affected_elem instanceof PhpParser\Node\Stmt\ClassMethod) {
|
||||
$add_or_delete[] = strtolower($name) . '::' . strtolower((string) $affected_elem->name);
|
||||
@ -231,10 +233,23 @@ class ClassStatementsDiffer extends AstDiffer
|
||||
$add_or_delete[] = strtolower($name . '&' . (string) $trait->getAttribute('resolvedName'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($diff_elem->type === DiffElem::TYPE_REMOVE) {
|
||||
if ($doc = $affected_elem->getDocComment()) {
|
||||
$start = $doc->getStartFilePos();
|
||||
} else {
|
||||
$start = (int)$affected_elem->getAttribute('startFilePos');
|
||||
}
|
||||
|
||||
$deletion_ranges[] = [
|
||||
$start,
|
||||
(int)$affected_elem->getAttribute('endFilePos')
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var array<int, array{0: int, 1: int, 2: int, 3: int}> $diff_map */
|
||||
return [$keep, $keep_signature, $add_or_delete, $diff_map];
|
||||
/** @var array<int, array{int, int, int, int}> $diff_map */
|
||||
return [$keep, $keep_signature, $add_or_delete, $diff_map, $deletion_ranges];
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ class FileStatementsDiffer extends AstDiffer
|
||||
* 0: list<string>,
|
||||
* 1: list<string>,
|
||||
* 2: list<string>,
|
||||
* 3: list<array{0: int, 1: int, 2: int, 3: int}>
|
||||
* 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
|
||||
@ -85,6 +86,7 @@ class FileStatementsDiffer extends AstDiffer
|
||||
$keep_signature = [];
|
||||
$add_or_delete = [];
|
||||
$diff_map = [];
|
||||
$deletion_ranges = [];
|
||||
|
||||
foreach ($diff as $diff_elem) {
|
||||
if ($diff_elem->type === DiffElem::TYPE_KEEP) {
|
||||
@ -103,6 +105,7 @@ class FileStatementsDiffer extends AstDiffer
|
||||
$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_
|
||||
@ -122,6 +125,7 @@ class FileStatementsDiffer extends AstDiffer
|
||||
$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_
|
||||
@ -136,6 +140,19 @@ class FileStatementsDiffer extends AstDiffer
|
||||
$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_
|
||||
@ -154,6 +171,6 @@ class FileStatementsDiffer extends AstDiffer
|
||||
}
|
||||
}
|
||||
|
||||
return [$keep, $keep_signature, $add_or_delete, $diff_map];
|
||||
return [$keep, $keep_signature, $add_or_delete, $diff_map, $deletion_ranges];
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ class NamespaceStatementsDiffer extends AstDiffer
|
||||
* 0: list<string>,
|
||||
* 1: list<string>,
|
||||
* 2: list<string>,
|
||||
* 3: list<array{0: int, 1: int, 2: int, 3: int}>
|
||||
* 3: list<array{int, int, int, int}>,
|
||||
* 4: list<array{int, int}>
|
||||
* }
|
||||
*/
|
||||
public static function diff(string $name, array $a, array $b, string $a_code, string $b_code): array
|
||||
@ -86,6 +87,7 @@ class NamespaceStatementsDiffer extends AstDiffer
|
||||
$keep_signature = [];
|
||||
$add_or_delete = [];
|
||||
$diff_map = [];
|
||||
$deletion_ranges = [];
|
||||
|
||||
foreach ($diff as $diff_elem) {
|
||||
if ($diff_elem->type === DiffElem::TYPE_KEEP) {
|
||||
@ -108,6 +110,7 @@ class NamespaceStatementsDiffer extends AstDiffer
|
||||
$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_
|
||||
@ -140,6 +143,6 @@ class NamespaceStatementsDiffer extends AstDiffer
|
||||
}
|
||||
}
|
||||
|
||||
return [$keep, $keep_signature, $add_or_delete, $diff_map];
|
||||
return [$keep, $keep_signature, $add_or_delete, $diff_map, $deletion_ranges];
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ use function substr_count;
|
||||
*/
|
||||
class PartialParserVisitor extends PhpParser\NodeVisitorAbstract
|
||||
{
|
||||
/** @var array<int, array{0: int, 1: int, 2: int, 3: int, 4:int}> */
|
||||
/** @var array<int, array{int, int, int, int, int}> */
|
||||
private $offset_map;
|
||||
|
||||
/** @var bool */
|
||||
@ -41,7 +41,7 @@ class PartialParserVisitor extends PhpParser\NodeVisitorAbstract
|
||||
/** @var PhpParser\ErrorHandler\Collecting */
|
||||
private $error_handler;
|
||||
|
||||
/** @param array<int, array{0: int, 1: int, 2: int, 3: int, 4:int}> $offset_map */
|
||||
/** @param array<int, array{int, int, int, int, int}> $offset_map */
|
||||
public function __construct(
|
||||
PhpParser\Parser $parser,
|
||||
PhpParser\ErrorHandler\Collecting $error_handler,
|
||||
|
@ -37,7 +37,7 @@ class SimpleNameResolver extends NodeVisitorAbstract
|
||||
* namespacedName attribute, as usual.)
|
||||
*
|
||||
* @param ErrorHandler $errorHandler Error handler
|
||||
* @param array<int, array{0: int, 1: int, 2: int, 3: int}> $offset_map
|
||||
* @param array<int, array{int, int, int, int}> $offset_map
|
||||
*/
|
||||
public function __construct(ErrorHandler $errorHandler, ?array $offset_map = null)
|
||||
{
|
||||
|
@ -61,10 +61,15 @@ class StatementsProvider
|
||||
private $errors = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array<int, array{0: int, 1: int, 2: int, 3: int}>>
|
||||
* @var array<string, array<int, array{int, int, int, int}>>
|
||||
*/
|
||||
private $diff_map = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array<int, array{int, int}>>
|
||||
*/
|
||||
private $deletion_ranges = [];
|
||||
|
||||
/**
|
||||
* @var PhpParser\Lexer|null
|
||||
*/
|
||||
@ -185,7 +190,7 @@ class StatementsProvider
|
||||
);
|
||||
|
||||
if ($existing_file_contents && $existing_statements && (!$has_errors || $stmts)) {
|
||||
[$unchanged_members, $unchanged_signature_members, $changed_members, $diff_map]
|
||||
[$unchanged_members, $unchanged_signature_members, $changed_members, $diff_map, $deletion_ranges]
|
||||
= \Psalm\Internal\Diff\FileStatementsDiffer::diff(
|
||||
$existing_statements,
|
||||
$stmts,
|
||||
@ -270,6 +275,7 @@ class StatementsProvider
|
||||
}
|
||||
|
||||
$this->diff_map[$file_path] = $diff_map;
|
||||
$this->deletion_ranges[$file_path] = $deletion_ranges;
|
||||
} elseif ($has_errors && !$stmts) {
|
||||
$this->errors[$file_path] = true;
|
||||
}
|
||||
@ -282,6 +288,7 @@ class StatementsProvider
|
||||
} else {
|
||||
$from_cache = true;
|
||||
$this->diff_map[$file_path] = [];
|
||||
$this->deletion_ranges[$file_path] = [];
|
||||
}
|
||||
|
||||
$this->parser_cache_provider->saveStatementsToCache($file_path, $file_content_hash, $stmts, $from_cache);
|
||||
@ -349,10 +356,14 @@ class StatementsProvider
|
||||
if (!isset($this->diff_map[$file_path])) {
|
||||
$this->diff_map[$file_path] = [];
|
||||
}
|
||||
|
||||
if (!isset($this->deletion_ranges[$file_path])) {
|
||||
$this->deletion_ranges[$file_path] = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<int, array{0: int, 1: int, 2: int, 3: int}>>
|
||||
* @return array<string, array<int, array{int, int, int, int}>>
|
||||
*/
|
||||
public function getDiffMap(): array
|
||||
{
|
||||
@ -360,7 +371,15 @@ class StatementsProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<int, array{0: int, 1: int, 2: int, 3: int}>> $diff_map
|
||||
* @return array<string, array<int, array{int, int}>>
|
||||
*/
|
||||
public function getDeletionRanges(): array
|
||||
{
|
||||
return $this->deletion_ranges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<int, array{int, int, int, int}>> $diff_map
|
||||
*
|
||||
*/
|
||||
public function addDiffMap(array $diff_map): void
|
||||
@ -368,12 +387,22 @@ class StatementsProvider
|
||||
$this->diff_map = array_merge($diff_map, $this->diff_map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<int, array{int, int}>> $deletion_ranges
|
||||
*
|
||||
*/
|
||||
public function addDeletionRanges(array $deletion_ranges): void
|
||||
{
|
||||
$this->deletion_ranges = array_merge($deletion_ranges, $this->deletion_ranges);
|
||||
}
|
||||
|
||||
public function resetDiffs(): void
|
||||
{
|
||||
$this->changed_members = [];
|
||||
$this->unchanged_members = [];
|
||||
$this->unchanged_signature_members = [];
|
||||
$this->diff_map = [];
|
||||
$this->deletion_ranges = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,8 @@ class FileDiffTest extends TestCase
|
||||
array $same_methods,
|
||||
array $same_signatures,
|
||||
array $changed_methods,
|
||||
array $diff_map_offsets
|
||||
array $diff_map_offsets,
|
||||
array $deletion_ranges
|
||||
): void {
|
||||
if (strpos($this->getTestName(), 'SKIPPED-') !== false) {
|
||||
$this->markTestSkipped();
|
||||
@ -65,6 +66,8 @@ class FileDiffTest extends TestCase
|
||||
);
|
||||
|
||||
$this->assertSame($diff_map_offsets, $found_offsets);
|
||||
|
||||
$this->assertSame($deletion_ranges, $diff[4]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,7 +202,7 @@ class FileDiffTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,array{string,string,string[],string[],string[],array<array-key,array{int,int}>}>
|
||||
* @return array<string,array{string,string,string[],string[],string[],array<array-key,array{int,int}>,list<array{int,int}>}>
|
||||
*/
|
||||
public function getChanges(): array
|
||||
{
|
||||
@ -239,6 +242,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
[],
|
||||
[[0, 0], [0, 0], [0, 0], [0, 0]],
|
||||
[],
|
||||
],
|
||||
'sameFileWithDoubleDocblocks' => [
|
||||
'<?php
|
||||
@ -293,6 +297,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
[],
|
||||
[[0, 0], [0, 0], [0, 0], [0, 0]],
|
||||
[],
|
||||
],
|
||||
'lineChanges' => [
|
||||
'<?php
|
||||
@ -334,6 +339,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
[],
|
||||
[[1, 1], [2, 2], [2, 2], [5, 5]],
|
||||
[],
|
||||
],
|
||||
'simpleBodyChangeWithoutSignatureChange' => [
|
||||
'<?php
|
||||
@ -362,6 +368,7 @@ class FileDiffTest extends TestCase
|
||||
['foo\a::foo'],
|
||||
[],
|
||||
[[1, 0]],
|
||||
[],
|
||||
],
|
||||
'simpleBodyChangesWithoutSignatureChange' => [
|
||||
'<?php
|
||||
@ -391,6 +398,7 @@ class FileDiffTest extends TestCase
|
||||
['foo\a::foo'],
|
||||
[],
|
||||
[[32, 1]],
|
||||
[],
|
||||
],
|
||||
'simpleBodyChangeWithSignatureChange' => [
|
||||
'<?php
|
||||
@ -419,6 +427,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]],
|
||||
[[182, 258]],
|
||||
],
|
||||
'propertyChange' => [
|
||||
'<?php
|
||||
@ -437,6 +446,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::$a', 'foo\a::$b'],
|
||||
[],
|
||||
[[84, 93]],
|
||||
],
|
||||
'propertyDefaultChange' => [
|
||||
'<?php
|
||||
@ -455,6 +465,7 @@ class FileDiffTest extends TestCase
|
||||
['foo\a::$a'],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
],
|
||||
'propertyDefaultAddition' => [
|
||||
'<?php
|
||||
@ -473,6 +484,7 @@ class FileDiffTest extends TestCase
|
||||
['foo\a::$a'],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
],
|
||||
'propertySignatureChange' => [
|
||||
'<?php
|
||||
@ -493,6 +505,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::$a', 'foo\a::$a'],
|
||||
[],
|
||||
[[84, 133]],
|
||||
],
|
||||
'propertyStaticChange' => [
|
||||
'<?php
|
||||
@ -513,6 +526,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::$a', 'foo\a::$a'],
|
||||
[],
|
||||
[[84, 140]],
|
||||
],
|
||||
'propertyVisibilityChange' => [
|
||||
'<?php
|
||||
@ -533,6 +547,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::$a', 'foo\a::$a'],
|
||||
[],
|
||||
[[84, 133]],
|
||||
],
|
||||
'addDocblockToFirst' => [
|
||||
'<?php
|
||||
@ -564,6 +579,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::foo', 'foo\a::foo'],
|
||||
[[84, 3]],
|
||||
[[84, 160]],
|
||||
],
|
||||
'addDocblockToSecond' => [
|
||||
'<?php
|
||||
@ -595,6 +611,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]],
|
||||
[[182, 258]],
|
||||
],
|
||||
'removeDocblock' => [
|
||||
'<?php
|
||||
@ -626,6 +643,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]],
|
||||
[[182, 342]],
|
||||
],
|
||||
'changeDocblock' => [
|
||||
'<?php
|
||||
@ -660,6 +678,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]],
|
||||
[[182, 344]],
|
||||
],
|
||||
'changeMethodVisibility' => [
|
||||
'<?php
|
||||
@ -688,6 +707,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]],
|
||||
[[182, 258]],
|
||||
],
|
||||
'removeFunctionAtEnd' => [
|
||||
'<?php
|
||||
@ -737,6 +757,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bat'],
|
||||
[[0, 0], [0, 0]],
|
||||
[[450, 610]],
|
||||
],
|
||||
'addSpaceInFunction' => [
|
||||
'<?php
|
||||
@ -811,6 +832,7 @@ class FileDiffTest extends TestCase
|
||||
['foo\a::bar'],
|
||||
[],
|
||||
[[0, 0], [4, 4]],
|
||||
[],
|
||||
],
|
||||
'removeSpaceInFunction' => [
|
||||
'<?php
|
||||
@ -885,6 +907,7 @@ class FileDiffTest extends TestCase
|
||||
['foo\a::bar'],
|
||||
[],
|
||||
[[0, 0], [-4, -4]],
|
||||
[],
|
||||
],
|
||||
'removeFunctionAtBeginning' => [
|
||||
'<?php
|
||||
@ -916,6 +939,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::foo'],
|
||||
[[-98, -3], [-98, -3]],
|
||||
[[84, 160]],
|
||||
],
|
||||
'removeFunctionInMiddle' => [
|
||||
'<?php
|
||||
@ -947,6 +971,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bar'],
|
||||
[[0, 0], [-98, -3]],
|
||||
[[182, 258]],
|
||||
],
|
||||
'changeNamespace' => [
|
||||
'<?php
|
||||
@ -972,6 +997,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
],
|
||||
'removeNamespace' => [
|
||||
'<?php
|
||||
@ -995,6 +1021,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
],
|
||||
'newFunctionAtEnd' => [
|
||||
'<?php
|
||||
@ -1044,6 +1071,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bat'],
|
||||
[[0, 0], [0, 0]],
|
||||
[],
|
||||
],
|
||||
'newFunctionAtBeginning' => [
|
||||
'<?php
|
||||
@ -1093,6 +1121,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bat'],
|
||||
[[183, 7], [183, 7]],
|
||||
[],
|
||||
],
|
||||
'newFunctionInMiddle' => [
|
||||
'<?php
|
||||
@ -1142,6 +1171,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::bat'],
|
||||
[[0, 0], [183, 7]],
|
||||
[],
|
||||
],
|
||||
'removeAdditionalComments' => [
|
||||
'<?php
|
||||
@ -1191,6 +1221,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['use:D', 'use:E', 'foo\a::foo', 'foo\a::bar', 'foo\a::foo', 'foo\a::bar'],
|
||||
[],
|
||||
[[84, 304], [327, 547]],
|
||||
],
|
||||
'SKIPPED-whiteSpaceOnly' => [
|
||||
'<?php
|
||||
@ -1221,6 +1252,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
],
|
||||
'changeDeclaredMethodId' => [
|
||||
'<?php
|
||||
@ -1255,6 +1287,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\b::__construct', 'foo\b::bar'],
|
||||
[[0, 0], [0, 0], [120, 2]],
|
||||
[],
|
||||
],
|
||||
'sameTrait' => [
|
||||
'<?php
|
||||
@ -1291,6 +1324,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
[],
|
||||
[[0, 0], [0, 0], [0, 0], [0, 0]],
|
||||
[],
|
||||
],
|
||||
'traitPropertyChange' => [
|
||||
'<?php
|
||||
@ -1309,6 +1343,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\t::$a', 'foo\t::$b'],
|
||||
[],
|
||||
[[84, 93]],
|
||||
],
|
||||
'traitMethodReturnTypeChange' => [
|
||||
'<?php
|
||||
@ -1339,6 +1374,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\t::barbar', 'foo\t::barbar'],
|
||||
[[-9, 0]],
|
||||
[[96, 199]],
|
||||
],
|
||||
'removeManyArguments' => [
|
||||
'<?php
|
||||
@ -1374,6 +1410,7 @@ class FileDiffTest extends TestCase
|
||||
['foo\c::barbar'],
|
||||
[],
|
||||
[[-36, -1]],
|
||||
[],
|
||||
],
|
||||
'docblockTwiceOver' => [
|
||||
'<?php
|
||||
@ -1424,6 +1461,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['bar\foo::b'],
|
||||
[[0, 0], [229, 8]],
|
||||
[],
|
||||
],
|
||||
'removeStatementsAbove' => [
|
||||
'<?php
|
||||
@ -1473,6 +1511,7 @@ class FileDiffTest extends TestCase
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
],
|
||||
'removeUse' => [
|
||||
'<?php
|
||||
@ -1497,6 +1536,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['use:Exception'],
|
||||
[[-36, -2]],
|
||||
[],
|
||||
],
|
||||
'addDocblockToFirstFunctionStatement' => [
|
||||
'<?php
|
||||
@ -1526,6 +1566,7 @@ class FileDiffTest extends TestCase
|
||||
['foo\c::foo'],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
],
|
||||
'vimeoDiff' => [
|
||||
'<?php
|
||||
@ -1596,6 +1637,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['c\a::foo', 'c\a::bar', 'c\a::zap', 'c\a::top', 'c\a::rot', 'c\a::bar'],
|
||||
[],
|
||||
[[124, 405], [432, 711], [738, 1016], [1043, 1284]],
|
||||
],
|
||||
'noUseChange' => [
|
||||
'<?php
|
||||
@ -1634,6 +1676,7 @@ class FileDiffTest extends TestCase
|
||||
['a\c::foo'],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
],
|
||||
'diffMultipleBadDocblocks' => [
|
||||
'<?php
|
||||
@ -1708,6 +1751,7 @@ class FileDiffTest extends TestCase
|
||||
[],
|
||||
['foo\a::foo', 'foo\a::bar', 'foo\a::foo', 'foo\a::bar'],
|
||||
[[-6, 0]],
|
||||
[[116, 428], [455, 756]],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
@ -100,4 +100,104 @@ class FileMapTest extends \Psalm\Tests\TestCase
|
||||
$type_map
|
||||
);
|
||||
}
|
||||
|
||||
public function testMapIsUpdatedAfterEditingMethod(): void
|
||||
{
|
||||
$codebase = $this->project_analyzer->getCodebase();
|
||||
$codebase->diff_methods = true;
|
||||
$config = $codebase->config;
|
||||
$config->throw_exception = false;
|
||||
|
||||
$this->addFile(
|
||||
'somefile.php',
|
||||
'<?php
|
||||
namespace Foo;
|
||||
|
||||
class A {
|
||||
public function first(\DateTimeImmutable $d) : void {
|
||||
echo $d->format("Y");
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
public function second(\DateTimeImmutable $d) : void {
|
||||
echo $d->format("Y");
|
||||
}
|
||||
}'
|
||||
);
|
||||
|
||||
$codebase->file_provider->openFile('somefile.php');
|
||||
$codebase->addFilesToAnalyze(['somefile.php' => 'somefile.php']);
|
||||
$codebase->scanFiles();
|
||||
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
|
||||
[$before] = $codebase->analyzer->getMapsForFile('somefile.php');
|
||||
|
||||
$codebase->addTemporaryFileChanges(
|
||||
'somefile.php',
|
||||
'<?php
|
||||
namespace Foo;
|
||||
|
||||
class A {
|
||||
public function first(\DateTimeImmutable $d) : void {
|
||||
echo $d->format("Y");
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
public function second(\DateTimeImmutable $d) : void {
|
||||
echo $d->format("Y");
|
||||
}
|
||||
}'
|
||||
);
|
||||
$codebase->reloadFiles($this->project_analyzer, ['somefile.php']);
|
||||
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
|
||||
[$after] = $codebase->analyzer->getMapsForFile('somefile.php');
|
||||
|
||||
$this->assertCount(\count($before), $after);
|
||||
}
|
||||
|
||||
public function testMapIsUpdatedAfterDeletingMethod(): void
|
||||
{
|
||||
$codebase = $this->project_analyzer->getCodebase();
|
||||
$codebase->diff_methods = true;
|
||||
$config = $codebase->config;
|
||||
$config->throw_exception = false;
|
||||
|
||||
$this->addFile(
|
||||
'somefile.php',
|
||||
'<?php
|
||||
namespace Foo;
|
||||
|
||||
class A {
|
||||
public function first(\DateTimeImmutable $d) : void {
|
||||
echo $d->format("Y");
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
public function second(\DateTimeImmutable $d) : void {
|
||||
echo $d->format("Y");
|
||||
}
|
||||
}'
|
||||
);
|
||||
|
||||
$codebase->file_provider->openFile('somefile.php');
|
||||
$codebase->addFilesToAnalyze(['somefile.php' => 'somefile.php']);
|
||||
$codebase->scanFiles();
|
||||
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
|
||||
$this->assertCount(8, $codebase->analyzer->getMapsForFile('somefile.php')[0]);
|
||||
|
||||
$codebase->addTemporaryFileChanges(
|
||||
'somefile.php',
|
||||
'<?php
|
||||
namespace Foo;
|
||||
|
||||
class A {
|
||||
public function second(\DateTimeImmutable $d) : void {
|
||||
echo $d->format("Y");
|
||||
}
|
||||
}'
|
||||
);
|
||||
$codebase->reloadFiles($this->project_analyzer, ['somefile.php']);
|
||||
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
|
||||
$this->assertCount(4, $codebase->analyzer->getMapsForFile('somefile.php')[0]);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user