From ad82c93edbd45dd66802cf418f98426c1d75cdba Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Sun, 28 Feb 2021 01:36:06 -0500 Subject: [PATCH] Fix #5297 - be more sensitive to additions and deletions in language server mode --- src/Psalm/Internal/Codebase/Analyzer.php | 180 +++++++++--------- src/Psalm/Internal/Codebase/Scanner.php | 7 +- .../Internal/Diff/ClassStatementsDiffer.php | 23 ++- .../Internal/Diff/FileStatementsDiffer.php | 21 +- .../Diff/NamespaceStatementsDiffer.php | 7 +- .../PhpVisitor/PartialParserVisitor.php | 4 +- .../PhpVisitor/SimpleNameResolver.php | 2 +- .../Internal/Provider/StatementsProvider.php | 37 +++- tests/FileDiffTest.php | 48 ++++- tests/LanguageServer/FileMapTest.php | 100 ++++++++++ 10 files changed, 322 insertions(+), 107 deletions(-) diff --git a/src/Psalm/Internal/Codebase/Analyzer.php b/src/Psalm/Internal/Codebase/Analyzer.php index cd000b37a..eb557cdde 100644 --- a/src/Psalm/Internal/Codebase/Analyzer.php +++ b/src/Psalm/Internal/Codebase/Analyzer.php @@ -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> $diff_map - * + * @param array> $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, + ]; + } } } } diff --git a/src/Psalm/Internal/Codebase/Scanner.php b/src/Psalm/Internal/Codebase/Scanner.php index 7af596ae0..85555dabc 100644 --- a/src/Psalm/Internal/Codebase/Scanner.php +++ b/src/Psalm/Internal/Codebase/Scanner.php @@ -54,7 +54,8 @@ use function substr; * issues:array>, * changed_members:array>, * unchanged_signature_members:array>, - * diff_map:array>, + * diff_map:array>, + * deletion_ranges:array>, * errors:array, * classlike_storage:array, * file_storage:array, @@ -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']) { diff --git a/src/Psalm/Internal/Diff/ClassStatementsDiffer.php b/src/Psalm/Internal/Diff/ClassStatementsDiffer.php index a832bd092..b15fa02a0 100644 --- a/src/Psalm/Internal/Diff/ClassStatementsDiffer.php +++ b/src/Psalm/Internal/Diff/ClassStatementsDiffer.php @@ -24,7 +24,8 @@ class ClassStatementsDiffer extends AstDiffer * 0: list, * 1: list, * 2: list, - * 3: array + * 3: array, + * 4: list * } */ 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 $diff_map */ - return [$keep, $keep_signature, $add_or_delete, $diff_map]; + /** @var array $diff_map */ + return [$keep, $keep_signature, $add_or_delete, $diff_map, $deletion_ranges]; } } diff --git a/src/Psalm/Internal/Diff/FileStatementsDiffer.php b/src/Psalm/Internal/Diff/FileStatementsDiffer.php index af09760e4..e2528554b 100644 --- a/src/Psalm/Internal/Diff/FileStatementsDiffer.php +++ b/src/Psalm/Internal/Diff/FileStatementsDiffer.php @@ -21,7 +21,8 @@ class FileStatementsDiffer extends AstDiffer * 0: list, * 1: list, * 2: list, - * 3: list + * 3: list, + * 4: list * } */ 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]; } } diff --git a/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php b/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php index b5c32120a..714b4af6e 100644 --- a/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php +++ b/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php @@ -21,7 +21,8 @@ class NamespaceStatementsDiffer extends AstDiffer * 0: list, * 1: list, * 2: list, - * 3: list + * 3: list, + * 4: list * } */ 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]; } } diff --git a/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php b/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php index 6954176fc..80466896e 100644 --- a/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php @@ -17,7 +17,7 @@ use function substr_count; */ class PartialParserVisitor extends PhpParser\NodeVisitorAbstract { - /** @var array */ + /** @var array */ private $offset_map; /** @var bool */ @@ -41,7 +41,7 @@ class PartialParserVisitor extends PhpParser\NodeVisitorAbstract /** @var PhpParser\ErrorHandler\Collecting */ private $error_handler; - /** @param array $offset_map */ + /** @param array $offset_map */ public function __construct( PhpParser\Parser $parser, PhpParser\ErrorHandler\Collecting $error_handler, diff --git a/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php b/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php index bcd00334f..e53d99fee 100644 --- a/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php +++ b/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php @@ -37,7 +37,7 @@ class SimpleNameResolver extends NodeVisitorAbstract * namespacedName attribute, as usual.) * * @param ErrorHandler $errorHandler Error handler - * @param array $offset_map + * @param array $offset_map */ public function __construct(ErrorHandler $errorHandler, ?array $offset_map = null) { diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index c59a3d214..b1295b5bf 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -61,10 +61,15 @@ class StatementsProvider private $errors = []; /** - * @var array> + * @var array> */ private $diff_map = []; + /** + * @var array> + */ + 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> + * @return array> */ public function getDiffMap(): array { @@ -360,7 +371,15 @@ class StatementsProvider } /** - * @param array> $diff_map + * @return array> + */ + public function getDeletionRanges(): array + { + return $this->deletion_ranges; + } + + /** + * @param array> $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> $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 = []; } /** diff --git a/tests/FileDiffTest.php b/tests/FileDiffTest.php index 05499c448..0597e9838 100644 --- a/tests/FileDiffTest.php +++ b/tests/FileDiffTest.php @@ -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}> + * @return array,list}> */ public function getChanges(): array { @@ -239,6 +242,7 @@ class FileDiffTest extends TestCase [], [], [[0, 0], [0, 0], [0, 0], [0, 0]], + [], ], 'sameFileWithDoubleDocblocks' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ 'project_analyzer->getCodebase(); + $codebase->diff_methods = true; + $config = $codebase->config; + $config->throw_exception = false; + + $this->addFile( + 'somefile.php', + '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', + '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', + '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', + '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]); + } }