2017-07-25 22:11:02 +02:00
|
|
|
<?php
|
2018-11-06 03:57:36 +01:00
|
|
|
namespace Psalm\Internal\Provider;
|
2017-07-25 22:11:02 +02:00
|
|
|
|
2019-07-05 22:24:00 +02:00
|
|
|
use function abs;
|
|
|
|
use function array_flip;
|
|
|
|
use function array_intersect_key;
|
|
|
|
use function array_map;
|
|
|
|
use function array_merge;
|
|
|
|
use function count;
|
|
|
|
use function filemtime;
|
|
|
|
use function md5;
|
2017-07-25 22:11:02 +02:00
|
|
|
use PhpParser;
|
2019-05-30 16:30:41 +02:00
|
|
|
use Psalm\Progress\Progress;
|
|
|
|
use Psalm\Progress\VoidProgress;
|
2019-06-26 22:52:29 +02:00
|
|
|
use function strlen;
|
|
|
|
use function substr;
|
2017-07-25 22:11:02 +02:00
|
|
|
|
2018-12-02 00:37:49 +01:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2017-07-25 22:11:02 +02:00
|
|
|
class StatementsProvider
|
|
|
|
{
|
2018-01-21 19:38:51 +01:00
|
|
|
/**
|
|
|
|
* @var FileProvider
|
|
|
|
*/
|
|
|
|
private $file_provider;
|
|
|
|
|
|
|
|
/**
|
2018-09-28 22:18:45 +02:00
|
|
|
* @var ?ParserCacheProvider
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2018-10-15 17:29:57 +02:00
|
|
|
public $parser_cache_provider;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
2018-03-12 05:01:21 +01:00
|
|
|
/**
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
private $this_modified_time;
|
|
|
|
|
|
|
|
/**
|
2018-09-28 22:18:45 +02:00
|
|
|
* @var ?FileStorageCacheProvider
|
2018-03-12 05:01:21 +01:00
|
|
|
*/
|
|
|
|
private $file_storage_cache_provider;
|
|
|
|
|
2018-09-26 00:37:24 +02:00
|
|
|
/**
|
|
|
|
* @var array<string, array<string, bool>>
|
|
|
|
*/
|
|
|
|
private $unchanged_members = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array<string, array<string, bool>>
|
|
|
|
*/
|
|
|
|
private $unchanged_signature_members = [];
|
|
|
|
|
2018-09-26 23:54:08 +02:00
|
|
|
/**
|
|
|
|
* @var array<string, array<string, bool>>
|
|
|
|
*/
|
|
|
|
private $changed_members = [];
|
|
|
|
|
2018-09-26 22:33:59 +02:00
|
|
|
/**
|
|
|
|
* @var array<string, array<int, array{0: int, 1: int, 2: int, 3: int}>>
|
|
|
|
*/
|
|
|
|
private $diff_map = [];
|
|
|
|
|
2019-05-17 17:22:34 +02:00
|
|
|
/**
|
|
|
|
* @var PhpParser\Lexer|null
|
|
|
|
*/
|
|
|
|
private static $lexer;
|
|
|
|
|
2019-05-27 19:14:50 +02:00
|
|
|
/**
|
|
|
|
* @var PhpParser\Parser|null
|
|
|
|
*/
|
|
|
|
private static $parser;
|
|
|
|
|
2018-03-12 05:01:21 +01:00
|
|
|
public function __construct(
|
|
|
|
FileProvider $file_provider,
|
2018-09-28 22:18:45 +02:00
|
|
|
ParserCacheProvider $parser_cache_provider = null,
|
|
|
|
FileStorageCacheProvider $file_storage_cache_provider = null
|
2018-03-12 05:01:21 +01:00
|
|
|
) {
|
2018-01-21 19:38:51 +01:00
|
|
|
$this->file_provider = $file_provider;
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->parser_cache_provider = $parser_cache_provider;
|
2018-03-12 05:01:21 +01:00
|
|
|
$this->this_modified_time = filemtime(__FILE__);
|
|
|
|
$this->file_storage_cache_provider = $file_storage_cache_provider;
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2017-07-25 22:11:02 +02:00
|
|
|
/**
|
2019-10-09 01:06:16 +02:00
|
|
|
* @return list<\PhpParser\Node\Stmt>
|
2017-07-25 22:11:02 +02:00
|
|
|
*/
|
2020-08-09 22:23:43 +02:00
|
|
|
public function getStatementsForFile(string $file_path, string $php_version, Progress $progress = null)
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2019-05-30 16:30:41 +02:00
|
|
|
if ($progress === null) {
|
|
|
|
$progress = new VoidProgress();
|
|
|
|
}
|
|
|
|
|
2017-07-25 22:11:02 +02:00
|
|
|
$from_cache = false;
|
|
|
|
|
2018-04-24 13:07:04 +02:00
|
|
|
$version = (string) PHP_PARSER_VERSION . $this->this_modified_time;
|
2017-07-25 22:11:02 +02:00
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
$file_contents = $this->file_provider->getContents($file_path);
|
|
|
|
$modified_time = $this->file_provider->getModifiedTime($file_path);
|
2017-07-25 22:11:02 +02:00
|
|
|
|
2020-03-19 00:54:37 +01:00
|
|
|
$config = \Psalm\Config::getInstance();
|
|
|
|
|
2020-03-19 01:21:02 +01:00
|
|
|
if (!$this->parser_cache_provider
|
2020-03-19 02:53:44 +01:00
|
|
|
|| (!$config->isInProjectDirs($file_path) && \strpos($file_path, 'vendor'))
|
2020-03-19 01:21:02 +01:00
|
|
|
) {
|
2019-05-30 16:30:41 +02:00
|
|
|
$progress->debug('Parsing ' . $file_path . "\n");
|
2018-09-28 22:18:45 +02:00
|
|
|
|
2020-08-09 22:23:43 +02:00
|
|
|
$stmts = self::parseStatements($file_contents, $php_version, $file_path);
|
2018-10-17 21:52:26 +02:00
|
|
|
|
|
|
|
return $stmts ?: [];
|
2018-09-28 22:18:45 +02:00
|
|
|
}
|
|
|
|
|
2017-07-25 22:11:02 +02:00
|
|
|
$file_content_hash = md5($version . $file_contents);
|
|
|
|
|
2018-09-28 22:18:45 +02:00
|
|
|
$stmts = $this->parser_cache_provider->loadStatementsFromCache(
|
2018-10-16 21:59:11 +02:00
|
|
|
$file_path,
|
2017-07-25 22:11:02 +02:00
|
|
|
$modified_time,
|
2018-10-16 21:59:11 +02:00
|
|
|
$file_content_hash
|
2017-07-25 22:11:02 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
if ($stmts === null) {
|
2019-05-30 16:30:41 +02:00
|
|
|
$progress->debug('Parsing ' . $file_path . "\n");
|
2017-07-25 22:11:02 +02:00
|
|
|
|
2018-10-26 06:59:14 +02:00
|
|
|
$existing_statements = $this->parser_cache_provider->loadExistingStatementsFromCache($file_path);
|
2018-09-26 00:37:24 +02:00
|
|
|
|
2019-12-11 23:08:02 +01:00
|
|
|
/** @psalm-suppress DocblockTypeContradiction */
|
2018-11-21 04:21:00 +01:00
|
|
|
if ($existing_statements && !$existing_statements[0] instanceof PhpParser\Node\Stmt) {
|
|
|
|
$existing_statements = null;
|
|
|
|
}
|
|
|
|
|
2018-10-16 21:59:11 +02:00
|
|
|
$existing_file_contents = $this->parser_cache_provider->loadExistingFileContentsFromCache($file_path);
|
2018-09-26 00:37:24 +02:00
|
|
|
|
2018-10-26 06:59:14 +02:00
|
|
|
// this happens after editing temporary file
|
|
|
|
if ($existing_file_contents === $file_contents && $existing_statements) {
|
|
|
|
$this->diff_map[$file_path] = [];
|
|
|
|
$this->parser_cache_provider->saveStatementsToCache(
|
|
|
|
$file_path,
|
|
|
|
$file_content_hash,
|
|
|
|
$existing_statements,
|
|
|
|
true
|
|
|
|
);
|
|
|
|
|
|
|
|
return $existing_statements;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_changes = null;
|
|
|
|
|
|
|
|
$existing_statements_copy = null;
|
|
|
|
|
2018-12-20 23:33:35 +01:00
|
|
|
if ($existing_statements
|
|
|
|
&& $existing_file_contents
|
2018-12-20 23:34:56 +01:00
|
|
|
&& abs(strlen($existing_file_contents) - strlen($file_contents)) < 5000
|
2018-12-20 23:33:35 +01:00
|
|
|
) {
|
2018-11-06 03:57:36 +01:00
|
|
|
$file_changes = \Psalm\Internal\Diff\FileDiffer::getDiff($existing_file_contents, $file_contents);
|
2018-10-31 03:25:31 +01:00
|
|
|
|
|
|
|
if (count($file_changes) < 10) {
|
|
|
|
$traverser = new PhpParser\NodeTraverser;
|
2020-03-15 04:54:42 +01:00
|
|
|
$traverser->addVisitor(new \Psalm\Internal\PhpVisitor\CloningVisitor);
|
2018-10-31 03:25:31 +01:00
|
|
|
// performs a deep clone
|
2019-10-09 01:06:16 +02:00
|
|
|
/** @var list<PhpParser\Node\Stmt> */
|
2018-10-31 03:25:31 +01:00
|
|
|
$existing_statements_copy = $traverser->traverse($existing_statements);
|
|
|
|
} else {
|
|
|
|
$file_changes = null;
|
|
|
|
}
|
2018-10-26 06:59:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$stmts = self::parseStatements(
|
|
|
|
$file_contents,
|
2020-08-09 22:23:43 +02:00
|
|
|
$php_version,
|
2018-10-26 06:59:14 +02:00
|
|
|
$file_path,
|
|
|
|
$existing_file_contents,
|
|
|
|
$existing_statements_copy,
|
|
|
|
$file_changes
|
|
|
|
);
|
|
|
|
|
2018-11-21 04:24:53 +01:00
|
|
|
if ($existing_file_contents && $existing_statements) {
|
2018-10-26 06:59:14 +02:00
|
|
|
list($unchanged_members, $unchanged_signature_members, $changed_members, $diff_map)
|
2018-11-06 03:57:36 +01:00
|
|
|
= \Psalm\Internal\Diff\FileStatementsDiffer::diff(
|
2018-10-26 06:59:14 +02:00
|
|
|
$existing_statements,
|
|
|
|
$stmts,
|
|
|
|
$existing_file_contents,
|
|
|
|
$file_contents
|
2018-09-26 00:37:24 +02:00
|
|
|
);
|
|
|
|
|
2018-10-26 06:59:14 +02:00
|
|
|
$unchanged_members = array_map(
|
|
|
|
/**
|
|
|
|
* @param int $_
|
2019-07-05 22:24:00 +02:00
|
|
|
*
|
2018-10-26 06:59:14 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
function ($_) {
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
array_flip($unchanged_members)
|
|
|
|
);
|
|
|
|
|
|
|
|
$unchanged_signature_members = array_map(
|
|
|
|
/**
|
|
|
|
* @param int $_
|
2019-07-05 22:24:00 +02:00
|
|
|
*
|
2018-10-26 06:59:14 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
function ($_) {
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
array_flip($unchanged_signature_members)
|
|
|
|
);
|
|
|
|
|
2018-10-27 05:04:38 +02:00
|
|
|
$file_path_hash = \md5($file_path);
|
|
|
|
|
|
|
|
$changed_members = array_map(
|
|
|
|
function (string $key) use ($file_path_hash) : string {
|
|
|
|
if (substr($key, 0, 4) === 'use:') {
|
|
|
|
return $key . ':' . $file_path_hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $key;
|
|
|
|
},
|
|
|
|
$changed_members
|
|
|
|
);
|
|
|
|
|
2018-10-26 06:59:14 +02:00
|
|
|
$changed_members = array_map(
|
|
|
|
/**
|
|
|
|
* @param int $_
|
2019-07-05 22:24:00 +02:00
|
|
|
*
|
2018-10-26 06:59:14 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
function ($_) {
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
array_flip($changed_members)
|
|
|
|
);
|
|
|
|
|
|
|
|
if (isset($this->unchanged_members[$file_path])) {
|
|
|
|
$this->unchanged_members[$file_path] = array_intersect_key(
|
|
|
|
$this->unchanged_members[$file_path],
|
|
|
|
$unchanged_members
|
2018-09-26 00:37:24 +02:00
|
|
|
);
|
2018-10-26 06:59:14 +02:00
|
|
|
} else {
|
|
|
|
$this->unchanged_members[$file_path] = $unchanged_members;
|
|
|
|
}
|
2018-09-26 00:37:24 +02:00
|
|
|
|
2018-10-26 06:59:14 +02:00
|
|
|
if (isset($this->unchanged_signature_members[$file_path])) {
|
|
|
|
$this->unchanged_signature_members[$file_path] = array_intersect_key(
|
|
|
|
$this->unchanged_signature_members[$file_path],
|
|
|
|
$unchanged_signature_members
|
2018-09-26 23:54:08 +02:00
|
|
|
);
|
2018-10-26 06:59:14 +02:00
|
|
|
} else {
|
|
|
|
$this->unchanged_signature_members[$file_path] = $unchanged_signature_members;
|
|
|
|
}
|
2018-09-26 23:54:08 +02:00
|
|
|
|
2018-10-26 06:59:14 +02:00
|
|
|
if (isset($this->changed_members[$file_path])) {
|
|
|
|
$this->changed_members[$file_path] = array_merge(
|
|
|
|
$this->changed_members[$file_path],
|
|
|
|
$changed_members
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$this->changed_members[$file_path] = $changed_members;
|
2018-09-26 00:37:24 +02:00
|
|
|
}
|
2018-10-26 06:59:14 +02:00
|
|
|
|
|
|
|
$this->diff_map[$file_path] = $diff_map;
|
2018-09-26 00:37:24 +02:00
|
|
|
}
|
|
|
|
|
2018-09-28 22:18:45 +02:00
|
|
|
if ($this->file_storage_cache_provider) {
|
|
|
|
$this->file_storage_cache_provider->removeCacheForFile($file_path);
|
|
|
|
}
|
|
|
|
|
2018-10-16 21:59:11 +02:00
|
|
|
$this->parser_cache_provider->cacheFileContents($file_path, $file_contents);
|
2017-07-25 22:11:02 +02:00
|
|
|
} else {
|
|
|
|
$from_cache = true;
|
2018-09-26 22:33:59 +02:00
|
|
|
$this->diff_map[$file_path] = [];
|
2017-07-25 22:11:02 +02:00
|
|
|
}
|
|
|
|
|
2018-10-16 21:59:11 +02:00
|
|
|
$this->parser_cache_provider->saveStatementsToCache($file_path, $file_content_hash, $stmts, $from_cache);
|
2017-07-25 22:11:02 +02:00
|
|
|
|
|
|
|
if (!$stmts) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
return $stmts;
|
|
|
|
}
|
|
|
|
|
2018-09-26 23:54:08 +02:00
|
|
|
/**
|
|
|
|
* @return array<string, array<string, bool>>
|
|
|
|
*/
|
|
|
|
public function getChangedMembers()
|
|
|
|
{
|
|
|
|
return $this->changed_members;
|
|
|
|
}
|
|
|
|
|
2018-10-11 19:58:39 +02:00
|
|
|
/**
|
|
|
|
* @param array<string, array<string, bool>> $more_changed_members
|
2019-07-05 22:24:00 +02:00
|
|
|
*
|
2018-10-11 19:58:39 +02:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function addChangedMembers(array $more_changed_members)
|
|
|
|
{
|
|
|
|
$this->changed_members = array_merge($more_changed_members, $this->changed_members);
|
|
|
|
}
|
|
|
|
|
2018-10-04 05:52:01 +02:00
|
|
|
/**
|
|
|
|
* @return array<string, array<string, bool>>
|
|
|
|
*/
|
|
|
|
public function getUnchangedSignatureMembers()
|
|
|
|
{
|
|
|
|
return $this->unchanged_signature_members;
|
|
|
|
}
|
|
|
|
|
2018-10-11 19:58:39 +02:00
|
|
|
/**
|
|
|
|
* @param array<string, array<string, bool>> $more_unchanged_members
|
2019-07-05 22:24:00 +02:00
|
|
|
*
|
2018-10-11 19:58:39 +02:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function addUnchangedSignatureMembers(array $more_unchanged_members)
|
|
|
|
{
|
|
|
|
$this->unchanged_signature_members = array_merge($more_unchanged_members, $this->unchanged_signature_members);
|
|
|
|
}
|
|
|
|
|
2018-09-26 22:33:59 +02:00
|
|
|
/**
|
|
|
|
* @param string $file_path
|
2019-07-05 22:24:00 +02:00
|
|
|
*
|
2018-09-26 22:33:59 +02:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setUnchangedFile($file_path)
|
|
|
|
{
|
|
|
|
if (!isset($this->diff_map[$file_path])) {
|
|
|
|
$this->diff_map[$file_path] = [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array<string, array<int, array{0: int, 1: int, 2: int, 3: int}>>
|
|
|
|
*/
|
|
|
|
public function getDiffMap()
|
|
|
|
{
|
|
|
|
return $this->diff_map;
|
|
|
|
}
|
|
|
|
|
2018-10-11 19:58:39 +02:00
|
|
|
/**
|
|
|
|
* @param array<string, array<int, array{0: int, 1: int, 2: int, 3: int}>> $diff_map
|
2019-07-05 22:24:00 +02:00
|
|
|
*
|
2018-10-11 19:58:39 +02:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function addDiffMap(array $diff_map)
|
|
|
|
{
|
|
|
|
$this->diff_map = array_merge($diff_map, $this->diff_map);
|
|
|
|
}
|
|
|
|
|
2018-09-30 05:51:06 +02:00
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function resetDiffs()
|
|
|
|
{
|
|
|
|
$this->changed_members = [];
|
|
|
|
$this->unchanged_members = [];
|
|
|
|
$this->unchanged_signature_members = [];
|
|
|
|
$this->diff_map = [];
|
|
|
|
}
|
|
|
|
|
2017-07-25 22:11:02 +02:00
|
|
|
/**
|
2018-10-17 21:52:26 +02:00
|
|
|
* @param string $file_contents
|
2018-09-29 06:15:39 +02:00
|
|
|
* @param string $file_path
|
2019-10-09 01:06:16 +02:00
|
|
|
* @param list<\PhpParser\Node\Stmt> $existing_statements
|
2018-10-26 06:59:14 +02:00
|
|
|
* @param array<int, array{0:int, 1:int, 2: int, 3: int, 4: int, 5:string}> $file_changes
|
2017-07-25 22:11:02 +02:00
|
|
|
*
|
2019-10-09 01:06:16 +02:00
|
|
|
* @return list<\PhpParser\Node\Stmt>
|
2017-07-25 22:11:02 +02:00
|
|
|
*/
|
2018-10-26 06:59:14 +02:00
|
|
|
public static function parseStatements(
|
|
|
|
$file_contents,
|
2020-08-09 22:23:43 +02:00
|
|
|
string $php_version,
|
2018-10-26 06:59:14 +02:00
|
|
|
$file_path = null,
|
|
|
|
string $existing_file_contents = null,
|
|
|
|
array $existing_statements = null,
|
|
|
|
array $file_changes = null
|
|
|
|
) {
|
2019-05-17 00:36:36 +02:00
|
|
|
$attributes = [
|
|
|
|
'comments', 'startLine', 'startFilePos', 'endFilePos',
|
|
|
|
];
|
2018-10-17 21:52:26 +02:00
|
|
|
|
2019-05-17 17:22:34 +02:00
|
|
|
if (!self::$lexer) {
|
2020-08-09 22:23:43 +02:00
|
|
|
self::$lexer = new PhpParser\Lexer\Emulative([
|
|
|
|
'usedAttributes' => $attributes,
|
|
|
|
'phpVersion' => $php_version,
|
|
|
|
]);
|
2019-05-17 17:22:34 +02:00
|
|
|
}
|
2017-07-25 22:11:02 +02:00
|
|
|
|
2019-05-27 19:14:50 +02:00
|
|
|
if (!self::$parser) {
|
2019-06-30 03:06:21 +02:00
|
|
|
self::$parser = (new PhpParser\ParserFactory())->create(PhpParser\ParserFactory::ONLY_PHP7, self::$lexer);
|
2019-05-27 19:14:50 +02:00
|
|
|
}
|
2017-07-25 22:11:02 +02:00
|
|
|
|
2018-10-26 06:59:14 +02:00
|
|
|
$used_cached_statements = false;
|
2017-07-25 22:11:02 +02:00
|
|
|
|
2018-09-29 06:15:39 +02:00
|
|
|
$error_handler = new \PhpParser\ErrorHandler\Collecting();
|
2018-10-04 00:16:33 +02:00
|
|
|
|
2018-10-26 06:59:14 +02:00
|
|
|
if ($existing_statements && $file_changes && $existing_file_contents) {
|
2020-03-15 04:54:42 +01:00
|
|
|
$clashing_traverser = new \Psalm\Internal\PhpTraverser\CustomTraverser;
|
|
|
|
$offset_analyzer = new \Psalm\Internal\PhpVisitor\PartialParserVisitor(
|
2019-05-27 19:14:50 +02:00
|
|
|
self::$parser,
|
2018-10-26 06:59:14 +02:00
|
|
|
$error_handler,
|
|
|
|
$file_changes,
|
|
|
|
$existing_file_contents,
|
|
|
|
$file_contents
|
|
|
|
);
|
2018-11-11 18:01:14 +01:00
|
|
|
$clashing_traverser->addVisitor($offset_analyzer);
|
2018-10-26 06:59:14 +02:00
|
|
|
$clashing_traverser->traverse($existing_statements);
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
if (!$offset_analyzer->mustRescan()) {
|
2018-10-26 06:59:14 +02:00
|
|
|
$used_cached_statements = true;
|
|
|
|
$stmts = $existing_statements;
|
|
|
|
} else {
|
2018-11-27 23:45:07 +01:00
|
|
|
try {
|
2019-10-09 01:06:16 +02:00
|
|
|
/** @var list<\PhpParser\Node\Stmt> */
|
2019-05-27 19:14:50 +02:00
|
|
|
$stmts = self::$parser->parse($file_contents, $error_handler) ?: [];
|
2018-11-27 23:45:07 +01:00
|
|
|
} catch (\Throwable $t) {
|
|
|
|
$stmts = [];
|
|
|
|
|
|
|
|
// hope this got caught below
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
try {
|
2019-10-09 01:06:16 +02:00
|
|
|
/** @var list<\PhpParser\Node\Stmt> */
|
2019-05-27 19:14:50 +02:00
|
|
|
$stmts = self::$parser->parse($file_contents, $error_handler) ?: [];
|
2018-11-27 23:45:07 +01:00
|
|
|
} catch (\Throwable $t) {
|
|
|
|
$stmts = [];
|
|
|
|
|
|
|
|
// hope this got caught below
|
2018-10-26 06:59:14 +02:00
|
|
|
}
|
|
|
|
}
|
2018-09-29 06:15:39 +02:00
|
|
|
|
|
|
|
if ($error_handler->hasErrors() && $file_path) {
|
|
|
|
$config = \Psalm\Config::getInstance();
|
|
|
|
|
|
|
|
foreach ($error_handler->getErrors() as $error) {
|
|
|
|
if ($error->hasColumnInfo()) {
|
|
|
|
\Psalm\IssueBuffer::add(
|
|
|
|
new \Psalm\Issue\ParseError(
|
|
|
|
$error->getMessage(),
|
|
|
|
new \Psalm\CodeLocation\ParseErrorLocation(
|
|
|
|
$error,
|
|
|
|
$file_contents,
|
|
|
|
$file_path,
|
|
|
|
$config->shortenFileName($file_path)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2017-07-25 22:11:02 +02:00
|
|
|
}
|
|
|
|
|
2019-06-30 03:06:21 +02:00
|
|
|
$error_handler->clearErrors();
|
|
|
|
|
2018-10-26 06:59:14 +02:00
|
|
|
$resolving_traverser = new PhpParser\NodeTraverser;
|
2020-03-15 04:54:42 +01:00
|
|
|
$name_resolver = new \Psalm\Internal\PhpVisitor\SimpleNameResolver(
|
2018-10-26 06:59:14 +02:00
|
|
|
$error_handler,
|
|
|
|
$used_cached_statements ? $file_changes : []
|
|
|
|
);
|
|
|
|
$resolving_traverser->addVisitor($name_resolver);
|
|
|
|
$resolving_traverser->traverse($stmts);
|
2018-09-26 00:37:24 +02:00
|
|
|
|
2017-07-25 22:11:02 +02:00
|
|
|
return $stmts;
|
|
|
|
}
|
2020-08-10 15:58:43 +02:00
|
|
|
|
|
|
|
public static function clearLexer() : void
|
|
|
|
{
|
|
|
|
self::$lexer = null;
|
|
|
|
}
|
2020-08-23 16:32:07 +02:00
|
|
|
|
|
|
|
public static function clearParser(): void
|
|
|
|
{
|
|
|
|
self::$parser = null;
|
|
|
|
}
|
2017-07-25 22:11:02 +02:00
|
|
|
}
|