1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00
psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php

1525 lines
49 KiB
PHP
Raw Normal View History

2016-06-09 18:08:25 -04:00
<?php
2018-11-05 21:57:36 -05:00
namespace Psalm\Internal\Analyzer;
2016-06-09 18:08:25 -04:00
2021-12-03 20:11:20 +01:00
use Amp\Loop;
2021-12-03 21:40:18 +01:00
use InvalidArgumentException;
use LogicException;
use Psalm\Codebase;
use Psalm\Config;
2017-01-12 00:54:41 -05:00
use Psalm\Context;
2021-12-03 20:29:06 +01:00
use Psalm\Exception\RefactorException;
use Psalm\Exception\UnsupportedIssueToFixException;
use Psalm\FileManipulation;
2021-06-08 05:55:21 +03:00
use Psalm\Internal\Codebase\TaintFlowGraph;
2021-12-03 20:11:20 +01:00
use Psalm\Internal\FileManipulation\FileManipulationBuffer;
use Psalm\Internal\LanguageServer\LanguageServer;
use Psalm\Internal\LanguageServer\ProtocolStreamReader;
use Psalm\Internal\LanguageServer\ProtocolStreamWriter;
2021-12-03 20:11:20 +01:00
use Psalm\Internal\MethodIdentifier;
2018-11-05 21:57:36 -05:00
use Psalm\Internal\Provider\ClassLikeStorageProvider;
use Psalm\Internal\Provider\FileProvider;
use Psalm\Internal\Provider\FileReferenceProvider;
use Psalm\Internal\Provider\ParserCacheProvider;
use Psalm\Internal\Provider\ProjectCacheProvider;
2018-11-05 21:57:36 -05:00
use Psalm\Internal\Provider\Providers;
2021-12-03 20:11:20 +01:00
use Psalm\Internal\Provider\StatementsProvider;
2021-06-08 05:55:21 +03:00
use Psalm\Issue\CodeIssue;
use Psalm\Issue\InvalidFalsableReturnType;
use Psalm\Issue\InvalidNullableReturnType;
use Psalm\Issue\InvalidReturnType;
use Psalm\Issue\LessSpecificReturnType;
use Psalm\Issue\MismatchingDocblockParamType;
use Psalm\Issue\MismatchingDocblockReturnType;
use Psalm\Issue\MissingClosureReturnType;
use Psalm\Issue\MissingParamType;
use Psalm\Issue\MissingPropertyType;
use Psalm\Issue\MissingReturnType;
2020-08-14 15:25:21 -04:00
use Psalm\Issue\ParamNameMismatch;
use Psalm\Issue\PossiblyUndefinedGlobalVariable;
use Psalm\Issue\PossiblyUndefinedVariable;
use Psalm\Issue\PossiblyUnusedMethod;
use Psalm\Issue\PossiblyUnusedProperty;
use Psalm\Issue\RedundantCast;
use Psalm\Issue\RedundantCastGivenDocblockType;
use Psalm\Issue\UnnecessaryVarAnnotation;
use Psalm\Issue\UnusedMethod;
use Psalm\Issue\UnusedProperty;
use Psalm\Issue\UnusedVariable;
use Psalm\Plugin\EventHandler\Event\AfterCodebasePopulatedEvent;
use Psalm\Progress\Progress;
use Psalm\Progress\VoidProgress;
use Psalm\Report;
use Psalm\Report\ReportOptions;
use Psalm\Type;
2021-12-03 21:40:18 +01:00
use ReflectionProperty;
2021-12-04 03:37:19 +01:00
use RuntimeException;
2021-12-03 21:40:18 +01:00
use UnexpectedValueException;
2021-06-08 05:55:21 +03:00
use function array_combine;
use function array_diff;
use function array_fill_keys;
use function array_keys;
use function array_map;
use function array_merge;
2021-06-08 05:55:21 +03:00
use function array_shift;
use function cli_set_process_title;
use function count;
2021-06-08 05:55:21 +03:00
use function defined;
use function dirname;
use function end;
use function explode;
2021-06-08 05:55:21 +03:00
use function extension_loaded;
use function file_exists;
2021-06-08 05:55:21 +03:00
use function file_get_contents;
use function filter_var;
2021-12-03 21:07:25 +01:00
use function function_exists;
2021-06-08 05:55:21 +03:00
use function fwrite;
use function implode;
use function in_array;
use function ini_get;
use function is_dir;
use function is_file;
use function is_int;
use function is_readable;
2021-06-08 05:55:21 +03:00
use function is_string;
use function microtime;
use function mkdir;
2021-12-03 21:07:25 +01:00
use function number_format;
2021-06-08 05:55:21 +03:00
use function pcntl_fork;
use function preg_match;
use function rename;
use function shell_exec;
use function stream_set_blocking;
use function stream_socket_accept;
use function stream_socket_client;
use function stream_socket_server;
use function strlen;
use function strpos;
use function strtolower;
use function substr;
use function substr_count;
2021-06-08 05:55:21 +03:00
use function trim;
use function usort;
2021-12-03 21:07:25 +01:00
use function version_compare;
2021-06-08 05:55:21 +03:00
use const FILTER_VALIDATE_INT;
use const PHP_EOL;
2021-12-03 21:25:22 +01:00
use const PHP_OS;
use const PHP_VERSION;
use const PSALM_VERSION;
2021-06-08 05:55:21 +03:00
use const STDERR;
use const STDIN;
use const STDOUT;
/**
* @internal
*/
2018-11-05 21:57:36 -05:00
class ProjectAnalyzer
2016-06-09 18:08:25 -04:00
{
2016-06-26 13:45:20 -04:00
/**
* Cached config
2016-11-02 02:29:00 -04:00
*
* @var Config
2016-06-26 13:45:20 -04:00
*/
2018-11-05 21:57:36 -05:00
private $config;
2016-12-07 22:38:57 -05:00
/**
* @var self
*/
public static $instance;
2016-06-26 13:45:20 -04:00
/**
* An object representing everything we know about the code
*
* @var Codebase
*/
2018-11-05 21:57:36 -05:00
private $codebase;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
/** @var FileProvider */
private $file_provider;
/** @var ClassLikeStorageProvider */
private $classlike_storage_provider;
/** @var ?ParserCacheProvider */
private $parser_cache_provider;
/** @var ?ProjectCacheProvider */
public $project_cache_provider;
/** @var FileReferenceProvider */
private $file_reference_provider;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
/**
* @var Progress
*/
public $progress;
2018-03-26 09:08:55 -04:00
/**
* @var bool
*/
public $debug_lines = false;
/**
* @var bool
*/
public $debug_performance = false;
/**
* @var bool
*/
public $show_issues = true;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
/** @var int */
public $threads;
2018-01-05 13:22:48 -05:00
/**
* @var array<string, bool>
2018-01-05 13:22:48 -05:00
*/
private $issues_to_fix = [];
2018-01-05 13:22:48 -05:00
2018-01-07 11:48:33 -05:00
/**
* @var bool
*/
public $dry_run = false;
/**
* @var bool
*/
public $full_run = false;
2018-01-07 16:11:51 -05:00
/**
* @var bool
*/
public $only_replace_php_types_with_non_docblock_types = false;
2018-10-30 18:58:22 -04:00
/**
* @var ?int
*/
public $onchange_line_limit;
/**
* @var bool
*/
public $provide_completion = false;
2019-04-29 12:07:34 -04:00
/**
* @var array<string,string>
*/
2020-04-01 12:56:32 -04:00
private $project_files = [];
2019-04-29 12:07:34 -04:00
/**
* @var array<string,string>
*/
private $extra_files = [];
/**
* @var array<string, string>
*/
private $to_refactor = [];
/**
* @var ?ReportOptions
*/
public $stdout_report_options;
/**
* @var array<ReportOptions>
*/
public $generated_report_options;
/**
* @var array<int, class-string<CodeIssue>>
*/
2020-09-20 18:54:46 +02:00
private const SUPPORTED_ISSUES_TO_FIX = [
InvalidFalsableReturnType::class,
InvalidNullableReturnType::class,
InvalidReturnType::class,
LessSpecificReturnType::class,
MismatchingDocblockParamType::class,
MismatchingDocblockReturnType::class,
MissingClosureReturnType::class,
MissingParamType::class,
MissingPropertyType::class,
MissingReturnType::class,
2020-08-14 15:25:21 -04:00
ParamNameMismatch::class,
PossiblyUndefinedGlobalVariable::class,
PossiblyUndefinedVariable::class,
PossiblyUnusedMethod::class,
PossiblyUnusedProperty::class,
RedundantCast::class,
RedundantCastGivenDocblockType::class,
UnusedMethod::class,
UnusedProperty::class,
UnusedVariable::class,
UnnecessaryVarAnnotation::class,
];
/**
* When this is true, the language server will send the diagnostic code with a help link.
*
* @var bool
*/
public $language_server_use_extended_diagnostic_codes = false;
/**
* If this is true then the language server will send log messages to the client with additional information.
*
* @var bool
*/
public $language_server_verbose = false;
2016-12-17 00:48:31 -05:00
/**
* @param array<ReportOptions> $generated_report_options
2016-12-17 00:48:31 -05:00
*/
public function __construct(
Config $config,
Providers $providers,
?ReportOptions $stdout_report_options = null,
array $generated_report_options = [],
int $threads = 1,
?Progress $progress = null
) {
if ($progress === null) {
$progress = new VoidProgress();
}
$this->parser_cache_provider = $providers->parser_cache_provider;
$this->project_cache_provider = $providers->project_cache_provider;
$this->file_provider = $providers->file_provider;
$this->classlike_storage_provider = $providers->classlike_storage_provider;
$this->file_reference_provider = $providers->file_reference_provider;
$this->progress = $progress;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
$this->threads = $threads;
$this->config = $config;
2016-12-07 22:38:57 -05:00
$this->clearCacheDirectoryIfConfigOrComposerLockfileChanged();
$this->codebase = new Codebase(
$config,
$providers,
$progress
);
$this->stdout_report_options = $stdout_report_options;
$this->generated_report_options = $generated_report_options;
$file_extensions = $this->config->getFileExtensions();
2019-04-29 12:07:34 -04:00
foreach ($this->config->getProjectDirectories() as $dir_name) {
$file_paths = $this->file_provider->getFilesInDir(
$dir_name,
$file_extensions,
[$this->config, 'isInProjectDirs']
);
2019-04-29 12:07:34 -04:00
foreach ($file_paths as $file_path) {
$this->addProjectFile($file_path);
2019-04-29 12:07:34 -04:00
}
}
foreach ($this->config->getExtraDirectories() as $dir_name) {
$file_paths = $this->file_provider->getFilesInDir(
$dir_name,
$file_extensions,
[$this->config, 'isInExtraDirs']
);
foreach ($file_paths as $file_path) {
$this->addExtraFile($file_path);
}
}
2019-04-29 12:07:34 -04:00
foreach ($this->config->getProjectFiles() as $file_path) {
2020-04-01 12:56:32 -04:00
$this->addProjectFile($file_path);
2019-04-29 12:07:34 -04:00
}
self::$instance = $this;
}
private function clearCacheDirectoryIfConfigOrComposerLockfileChanged(): void
{
if ($this->project_cache_provider
&& $this->project_cache_provider->hasLockfileChanged()
) {
$this->progress->debug(
'Composer lockfile change detected, clearing cache' . "\n"
);
$cache_directory = $this->config->getCacheDirectory();
if ($cache_directory !== null) {
Config::removeCacheDirectory($cache_directory);
}
if ($this->file_reference_provider->cache) {
$this->file_reference_provider->cache->hasConfigChanged();
}
$this->project_cache_provider->updateComposerLockHash();
} elseif ($this->file_reference_provider->cache
&& $this->file_reference_provider->cache->hasConfigChanged()
) {
$this->progress->debug(
'Config change detected, clearing cache' . "\n"
);
$cache_directory = $this->config->getCacheDirectory();
if ($cache_directory !== null) {
Config::removeCacheDirectory($cache_directory);
}
if ($this->project_cache_provider) {
$this->project_cache_provider->hasLockfileChanged();
}
}
2016-12-07 22:38:57 -05:00
}
/**
* @param array<string> $report_file_paths
2020-10-17 18:36:44 +02:00
* @return list<ReportOptions>
*/
public static function getFileReportOptions(array $report_file_paths, bool $show_info = true): array
{
$report_options = [];
$mapping = [
'checkstyle.xml' => Report::TYPE_CHECKSTYLE,
'sonarqube.json' => Report::TYPE_SONARQUBE,
2021-01-16 06:48:35 +01:00
'codeclimate.json' => Report::TYPE_CODECLIMATE,
'summary.json' => Report::TYPE_JSON_SUMMARY,
'junit.xml' => Report::TYPE_JUNIT,
'.xml' => Report::TYPE_XML,
'.json' => Report::TYPE_JSON,
'.txt' => Report::TYPE_TEXT,
'.emacs' => Report::TYPE_EMACS,
'.pylint' => Report::TYPE_PYLINT,
'.console' => Report::TYPE_CONSOLE,
'.sarif' => Report::TYPE_SARIF,
];
foreach ($report_file_paths as $report_file_path) {
foreach ($mapping as $extension => $type) {
if (substr($report_file_path, -strlen($extension)) === $extension) {
$o = new ReportOptions();
$o->format = $type;
$o->show_info = $show_info;
$o->output_path = $report_file_path;
$o->use_color = false;
$report_options[] = $o;
continue 2;
}
}
2021-12-03 21:40:18 +01:00
throw new UnexpectedValueException('Unknown report format ' . $report_file_path);
}
return $report_options;
}
private function visitAutoloadFiles(): void
{
$start_time = microtime(true);
$this->config->visitComposerAutoloadFiles($this, $this->progress);
$now_time = microtime(true);
$this->progress->debug(
2021-12-03 21:07:25 +01:00
'Visiting autoload files took ' . number_format($now_time - $start_time, 3) . 's' . "\n"
);
}
2020-10-12 21:46:47 +02:00
public function server(?string $address = '127.0.0.1:12345', bool $socket_server_mode = false): void
{
$this->visitAutoloadFiles();
2018-11-05 21:57:36 -05:00
$this->codebase->diff_methods = true;
$this->file_reference_provider->loadReferenceCache();
$this->codebase->enterServerMode();
2021-12-03 21:07:25 +01:00
if (ini_get('pcre.jit') === '1'
2021-12-03 21:25:22 +01:00
&& PHP_OS === 'Darwin'
&& version_compare(PHP_VERSION, '7.3.0') >= 0
&& version_compare(PHP_VERSION, '7.4.0') < 0
) {
// do nothing
} else {
$cpu_count = self::getCpuCount();
// let's not go crazy
$usable_cpus = $cpu_count - 2;
if ($usable_cpus > 1) {
$this->threads = $usable_cpus;
}
}
$this->config->initializePlugins($this);
foreach ($this->config->getProjectDirectories() as $dir_name) {
$this->checkDirWithConfig($dir_name, $this->config);
}
@cli_set_process_title('Psalm ' . PSALM_VERSION . ' - PHP Language Server');
if (!$socket_server_mode && $address) {
// Connect to a TCP server
$socket = stream_socket_client('tcp://' . $address, $errno, $errstr);
if ($socket === false) {
fwrite(STDERR, "Could not connect to language client. Error $errno\n$errstr");
exit(1);
}
stream_set_blocking($socket, false);
new LanguageServer(
new ProtocolStreamReader($socket),
new ProtocolStreamWriter($socket),
$this
);
2021-12-03 20:11:20 +01:00
Loop::run();
} elseif ($socket_server_mode && $address) {
// Run a TCP Server
$tcpServer = stream_socket_server('tcp://' . $address, $errno, $errstr);
if ($tcpServer === false) {
fwrite(STDERR, "Could not listen on $address. Error $errno\n$errstr");
exit(1);
}
fwrite(STDOUT, "Server listening on $address\n");
$fork_available = true;
if (!extension_loaded('pcntl')) {
fwrite(STDERR, "PCNTL is not available. Only a single connection will be accepted\n");
$fork_available = false;
}
$disabled_functions = array_map('trim', explode(',', ini_get('disable_functions')));
if (in_array('pcntl_fork', $disabled_functions)) {
fwrite(
STDERR,
"pcntl_fork() is disabled by php configuration (disable_functions directive)."
. " Only a single connection will be accepted\n"
);
$fork_available = false;
}
while ($socket = stream_socket_accept($tcpServer, -1)) {
fwrite(STDOUT, "Connection accepted\n");
stream_set_blocking($socket, false);
if ($fork_available) {
// If PCNTL is available, fork a child process for the connection
// An exit notification will only terminate the child process
$pid = pcntl_fork();
if ($pid === -1) {
fwrite(STDERR, "Could not fork\n");
exit(1);
}
if ($pid === 0) {
// Child process
$reader = new ProtocolStreamReader($socket);
$reader->on(
'close',
function (): void {
fwrite(STDOUT, "Connection closed\n");
}
);
new LanguageServer(
$reader,
new ProtocolStreamWriter($socket),
$this
);
// Just for safety
exit(0);
}
} else {
// If PCNTL is not available, we only accept one connection.
// An exit notification will terminate the server
new LanguageServer(
new ProtocolStreamReader($socket),
new ProtocolStreamWriter($socket),
$this
);
2021-12-03 20:11:20 +01:00
Loop::run();
}
}
} else {
// Use STDIO
stream_set_blocking(STDIN, false);
new LanguageServer(
new ProtocolStreamReader(STDIN),
new ProtocolStreamWriter(STDOUT),
$this
);
2021-12-03 20:11:20 +01:00
Loop::run();
}
}
public static function getInstance(): ProjectAnalyzer
2016-12-07 22:38:57 -05:00
{
return self::$instance;
}
public function canReportIssues(string $file_path): bool
2019-04-29 12:07:34 -04:00
{
return isset($this->project_files[$file_path]);
}
private function generatePHPVersionMessage(): string
{
$codebase = $this->codebase;
$version = $codebase->php_major_version . '.' . $codebase->php_minor_version;
switch ($codebase->php_version_source) {
case 'cli':
$source = '(set by CLI argument)';
break;
case 'config':
$source = '(set by config file)';
break;
case 'composer':
$source = '(inferred from composer.json)';
break;
case 'tests':
$source = '(set by tests)';
break;
case 'runtime':
$source = '(inferred from current PHP version)';
break;
}
return "Target PHP version: $version $source\n";
}
public function check(string $base_dir, bool $is_diff = false): void
2016-06-13 15:33:18 -04:00
{
$start_checks = (int)microtime(true);
if (!$base_dir) {
2021-12-03 21:40:18 +01:00
throw new InvalidArgumentException('Cannot work with empty base_dir');
2016-10-15 00:12:57 -04:00
}
2016-10-07 00:58:08 -04:00
$diff_files = null;
2016-10-07 13:26:29 -04:00
$deleted_files = null;
2019-04-27 17:38:24 -04:00
$this->full_run = true;
$reference_cache = $this->file_reference_provider->loadReferenceCache(true);
2018-10-06 20:11:19 -04:00
2020-03-26 18:14:54 -04:00
$this->codebase->diff_methods = $is_diff;
if ($is_diff
2018-10-06 20:11:19 -04:00
&& $reference_cache
&& $this->project_cache_provider
&& $this->project_cache_provider->canDiffFiles()
) {
$deleted_files = $this->file_reference_provider->getDeletedReferencedFiles();
$diff_files = array_merge($deleted_files, $this->getDiffFiles());
}
2016-10-07 00:58:08 -04:00
$this->progress->write($this->generatePHPVersionMessage());
$this->progress->startScanningFiles();
$diff_no_files = false;
2019-04-27 17:38:24 -04:00
if ($diff_files === null
|| $deleted_files === null
|| count($diff_files) > 200
) {
$this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
2020-03-31 22:32:48 -04:00
$this->visitAutoloadFiles();
$this->codebase->scanner->addFilesToShallowScan($this->extra_files);
2020-03-31 23:05:20 -04:00
$this->codebase->scanner->addFilesToDeepScan($this->project_files);
$this->codebase->analyzer->addFilesToAnalyze($this->project_files);
$this->config->initializePlugins($this);
$this->codebase->scanFiles($this->threads);
$this->codebase->infer_types_from_usage = true;
2016-11-02 02:29:00 -04:00
} else {
$this->progress->debug(count($diff_files) . ' changed files: ' . "\n");
$this->progress->debug(' ' . implode("\n ", $diff_files) . "\n");
$this->codebase->analyzer->addFilesToShowResults($this->project_files);
if ($diff_files) {
$file_list = $this->getReferencedFilesFromDiff($diff_files);
2016-11-02 02:29:00 -04:00
2018-02-19 23:36:29 -05:00
// strip out deleted files
$file_list = array_diff($file_list, $deleted_files);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
if ($file_list) {
$this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
$this->visitAutoloadFiles();
2020-04-01 15:57:25 -04:00
$this->checkDiffFilesWithConfig($this->config, $file_list);
$this->config->initializePlugins($this);
$this->codebase->scanFiles($this->threads);
} else {
$diff_no_files = true;
}
} else {
$diff_no_files = true;
2018-02-19 23:36:29 -05:00
}
}
2017-02-15 00:15:51 -05:00
if (!$diff_no_files) {
$this->config->visitStubFiles($this->codebase, $this->progress);
$event = new AfterCodebasePopulatedEvent($this->codebase);
$this->config->eventDispatcher->dispatchAfterCodebasePopulated($event);
}
$this->progress->startAnalyzingFiles();
2020-04-01 12:56:32 -04:00
$this->codebase->analyzer->analyzeFiles(
$this,
$this->threads,
$this->codebase->alter_code,
true
);
2021-12-06 18:17:52 +01:00
if ($this->parser_cache_provider && !$is_diff) {
$removed_parser_files = $this->parser_cache_provider->deleteOldParserCaches($start_checks);
if ($removed_parser_files) {
$this->progress->debug('Removed ' . $removed_parser_files . ' old parser caches' . "\n");
}
}
2018-01-21 23:42:57 -05:00
}
public function consolidateAnalyzedData(): void
2018-01-21 23:42:57 -05:00
{
$this->codebase->classlikes->consolidateAnalyzedData(
$this->codebase->methods,
$this->progress,
2021-09-26 22:39:01 +02:00
(bool)$this->codebase->find_unused_code
);
2018-01-21 23:42:57 -05:00
}
public function trackTaintedInputs(): void
{
$this->codebase->taint_flow_graph = new TaintFlowGraph();
}
public function trackUnusedSuppressions(): void
2019-08-18 14:27:50 -04:00
{
$this->codebase->track_unused_suppressions = true;
}
public function interpretRefactors(): void
{
if (!$this->codebase->alter_code) {
2021-12-03 21:40:18 +01:00
throw new UnexpectedValueException('Should not be checking references');
}
// interpret wildcards
foreach ($this->to_refactor as $source => $destination) {
if (($source_pos = strpos($source, '*'))
&& ($destination_pos = strpos($destination, '*'))
&& $source_pos === (strlen($source) - 1)
&& $destination_pos === (strlen($destination) - 1)
) {
foreach ($this->codebase->classlike_storage_provider->getAll() as $class_storage) {
2021-09-26 22:51:44 +02:00
if (strpos($source, substr($class_storage->name, 0, $source_pos)) === 0) {
$this->to_refactor[$class_storage->name]
= substr($destination, 0, -1) . substr($class_storage->name, $source_pos);
}
}
unset($this->to_refactor[$source]);
}
}
foreach ($this->to_refactor as $source => $destination) {
$source_parts = explode('::', $source);
$destination_parts = explode('::', $destination);
2019-06-04 16:36:32 -04:00
if (!$this->codebase->classlikes->hasFullyQualifiedClassName($source_parts[0])) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Source class ' . $source_parts[0] . ' doesnt exist'
);
}
2019-06-04 16:36:32 -04:00
if (count($source_parts) === 1 && count($destination_parts) === 1) {
if ($this->codebase->classlikes->hasFullyQualifiedClassName($destination_parts[0])) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 16:36:32 -04:00
'Destination class ' . $destination_parts[0] . ' already exists'
);
}
$source_class_storage = $this->codebase->classlike_storage_provider->get($source_parts[0]);
$destination_parts = explode('\\', $destination, -1);
2019-06-04 16:36:32 -04:00
$destination_ns = implode('\\', $destination_parts);
$this->codebase->classes_to_move[strtolower($source)] = $destination;
$destination_class_storage = $this->codebase->classlike_storage_provider->create($destination);
$destination_class_storage->name = $destination;
if ($source_class_storage->aliases) {
$destination_class_storage->aliases = clone $source_class_storage->aliases;
$destination_class_storage->aliases->namespace = $destination_ns;
}
$destination_class_storage->location = $source_class_storage->location;
$destination_class_storage->stmt_location = $source_class_storage->stmt_location;
$destination_class_storage->populated = true;
$this->codebase->class_transforms[strtolower($source)] = $destination;
continue;
}
2021-12-03 20:11:20 +01:00
$source_method_id = new MethodIdentifier(
$source_parts[0],
strtolower($source_parts[1])
);
if ($this->codebase->methods->methodExists($source_method_id)) {
if ($this->codebase->methods->methodExists(
2021-12-03 20:11:20 +01:00
new MethodIdentifier(
$destination_parts[0],
strtolower($destination_parts[1])
)
)) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Destination method ' . $destination . ' already exists'
);
2019-06-04 00:32:19 -04:00
}
if (!$this->codebase->classlikes->classExists($destination_parts[0])) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
'Destination class ' . $destination_parts[0] . ' doesnt exist'
);
}
2021-10-13 18:29:25 +02:00
$source_lc = strtolower($source);
if (strtolower($source_parts[0]) !== strtolower($destination_parts[0])) {
$source_method_storage = $this->codebase->methods->getStorage($source_method_id);
$destination_class_storage
= $this->codebase->classlike_storage_provider->get($destination_parts[0]);
if (!$source_method_storage->is_static
&& !isset(
$destination_class_storage->parent_classes[strtolower($source_method_id->fq_class_name)]
)
) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
'Cannot move non-static method ' . $source
. ' into unrelated class ' . $destination_parts[0]
);
}
2021-10-13 18:29:25 +02:00
$this->codebase->methods_to_move[$source_lc]= $destination;
} else {
2021-10-13 18:29:25 +02:00
$this->codebase->methods_to_rename[$source_lc] = $destination_parts[1];
}
2021-10-13 18:29:25 +02:00
$this->codebase->call_transforms[$source_lc . '\((.*\))'] = $destination . '($1)';
continue;
}
2019-06-04 00:32:19 -04:00
if ($source_parts[1][0] === '$') {
if ($destination_parts[1][0] !== '$') {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Destination property must be of the form Foo::$bar'
);
}
if (!$this->codebase->properties->propertyExists($source, true)) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Property ' . $source . ' does not exist'
);
}
if ($this->codebase->properties->propertyExists($destination, true)) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Destination property ' . $destination . ' already exists'
);
}
if (!$this->codebase->classlikes->classExists($destination_parts[0])) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Destination class ' . $destination_parts[0] . ' doesnt exist'
);
}
$source_id = strtolower($source_parts[0]) . '::' . $source_parts[1];
if (strtolower($source_parts[0]) !== strtolower($destination_parts[0])) {
$source_storage = $this->codebase->properties->getStorage($source);
if (!$source_storage->is_static) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Cannot move non-static property ' . $source
);
}
$this->codebase->properties_to_move[$source_id] = $destination;
} else {
$this->codebase->properties_to_rename[$source_id] = substr($destination_parts[1], 1);
}
$this->codebase->property_transforms[$source_id] = $destination;
continue;
}
$source_class_constants = $this->codebase->classlikes->getConstantsForClass(
$source_parts[0],
2021-12-03 21:40:18 +01:00
ReflectionProperty::IS_PRIVATE
2019-06-04 00:32:19 -04:00
);
if (isset($source_class_constants[$source_parts[1]])) {
2019-06-04 16:36:32 -04:00
if (!$this->codebase->classlikes->hasFullyQualifiedClassName($destination_parts[0])) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Destination class ' . $destination_parts[0] . ' doesnt exist'
);
}
$destination_class_constants = $this->codebase->classlikes->getConstantsForClass(
$destination_parts[0],
2021-12-03 21:40:18 +01:00
ReflectionProperty::IS_PRIVATE
2019-06-04 00:32:19 -04:00
);
if (isset($destination_class_constants[$destination_parts[1]])) {
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Destination constant ' . $destination . ' already exists'
);
}
$source_id = strtolower($source_parts[0]) . '::' . $source_parts[1];
if (strtolower($source_parts[0]) !== strtolower($destination_parts[0])) {
$this->codebase->class_constants_to_move[$source_id] = $destination;
} else {
$this->codebase->class_constants_to_rename[$source_id] = $destination_parts[1];
}
$this->codebase->class_constant_transforms[$source_id] = $destination;
2019-06-04 11:14:49 -04:00
continue;
2019-06-04 00:32:19 -04:00
}
2021-12-03 20:29:06 +01:00
throw new RefactorException(
2019-06-04 00:32:19 -04:00
'Psalm cannot locate ' . $source
);
}
}
public function prepareMigration(): void
{
if (!$this->codebase->alter_code) {
2021-12-03 21:40:18 +01:00
throw new UnexpectedValueException('Should not be checking references');
}
2019-06-02 12:02:32 -04:00
$this->codebase->classlikes->moveMethods(
$this->codebase->methods,
$this->progress
);
2019-06-04 00:32:19 -04:00
$this->codebase->classlikes->moveProperties(
$this->codebase->properties,
$this->progress
);
2019-06-04 11:14:49 -04:00
2019-06-04 16:36:32 -04:00
$this->codebase->classlikes->moveClassConstants(
2019-06-04 11:14:49 -04:00
$this->progress
);
}
public function migrateCode(): void
{
if (!$this->codebase->alter_code) {
2021-12-03 21:40:18 +01:00
throw new UnexpectedValueException('Should not be checking references');
}
2021-12-03 20:11:20 +01:00
$migration_manipulations = FileManipulationBuffer::getMigrationManipulations(
$this->codebase->file_provider
);
2019-06-05 00:14:25 -04:00
if ($migration_manipulations) {
foreach ($migration_manipulations as $file_path => $file_manipulations) {
usort(
$file_manipulations,
function (FileManipulation $a, FileManipulation $b): int {
2019-06-05 00:14:25 -04:00
if ($a->start === $b->start) {
if ($b->end === $a->end) {
return $b->insertion_text > $a->insertion_text ? 1 : -1;
}
2019-06-05 00:14:25 -04:00
return $b->end > $a->end ? 1 : -1;
}
2019-06-05 00:14:25 -04:00
return $b->start > $a->start ? 1 : -1;
}
2019-06-05 00:14:25 -04:00
);
2019-06-05 00:14:25 -04:00
$existing_contents = $this->codebase->file_provider->getContents($file_path);
2019-06-05 00:14:25 -04:00
foreach ($file_manipulations as $manipulation) {
$existing_contents = $manipulation->transform($existing_contents);
}
2019-06-05 00:14:25 -04:00
$this->codebase->file_provider->setContents($file_path, $existing_contents);
}
2019-06-05 00:14:25 -04:00
}
if ($this->codebase->classes_to_move) {
foreach ($this->codebase->classes_to_move as $source => $destination) {
$source_class_storage = $this->codebase->classlike_storage_provider->get($source);
if (!$source_class_storage->location) {
continue;
}
2019-06-05 00:14:25 -04:00
$potential_file_path = $this->config->getPotentialComposerFilePathForClassLike($destination);
if ($potential_file_path && !file_exists($potential_file_path)) {
$containing_dir = dirname($potential_file_path);
if (!file_exists($containing_dir)) {
mkdir($containing_dir, 0777, true);
}
2019-06-05 00:14:25 -04:00
rename($source_class_storage->location->file_path, $potential_file_path);
}
}
}
}
public function findReferencesTo(string $symbol): void
2018-01-21 23:42:57 -05:00
{
if (!$this->stdout_report_options) {
2021-12-03 21:40:18 +01:00
throw new UnexpectedValueException('Not expecting to emit output');
}
$locations = $this->codebase->findReferencesToSymbol($symbol);
foreach ($locations as $location) {
$snippet = $location->getSnippet();
2017-03-01 22:27:52 -05:00
$snippet_bounds = $location->getSnippetBounds();
$selection_bounds = $location->getSelectionBounds();
2017-03-01 22:27:52 -05:00
$selection_start = $selection_bounds[0] - $snippet_bounds[0];
$selection_length = $selection_bounds[1] - $selection_bounds[0];
echo $location->file_name . ':' . $location->getLineNumber() . "\n" .
(
$this->stdout_report_options->use_color
? substr($snippet, 0, $selection_start) .
"\e[97;42m" . substr($snippet, $selection_start, $selection_length) .
"\e[0m" . substr($snippet, $selection_length + $selection_start)
: $snippet
) . "\n" . "\n";
}
}
public function checkDir(string $dir_name): void
{
$this->file_reference_provider->loadReferenceCache();
$this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
$this->checkDirWithConfig($dir_name, $this->config, true);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
$this->progress->write($this->generatePHPVersionMessage());
$this->progress->startScanningFiles();
$this->config->initializePlugins($this);
$this->codebase->scanFiles($this->threads);
$this->config->visitStubFiles($this->codebase, $this->progress);
$this->progress->startAnalyzingFiles();
2020-04-01 12:56:32 -04:00
$this->codebase->analyzer->analyzeFiles(
$this,
$this->threads,
$this->codebase->alter_code,
$this->codebase->find_unused_code === 'always'
);
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
private function checkDirWithConfig(string $dir_name, Config $config, bool $allow_non_project_files = false): void
{
$file_extensions = $config->getFileExtensions();
$directory_filter = $allow_non_project_files ? null : [$this->config, 'isInProjectDirs'];
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
$file_paths = $this->file_provider->getFilesInDir(
$dir_name,
$file_extensions,
$directory_filter
);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
$files_to_scan = [];
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
foreach ($file_paths as $file_path) {
$files_to_scan[$file_path] = $file_path;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
}
2018-02-03 18:52:35 -05:00
$this->codebase->addFilesToAnalyze($files_to_scan);
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
public function addProjectFile(string $file_path): void
2020-04-01 12:56:32 -04:00
{
$this->project_files[$file_path] = $file_path;
}
public function addExtraFile(string $file_path): void
{
$this->extra_files[$file_path] = $file_path;
}
2020-04-01 12:56:32 -04:00
/**
2020-10-17 18:36:44 +02:00
* @return list<string>
*/
protected function getDiffFiles(): array
{
if (!$this->parser_cache_provider || !$this->project_cache_provider) {
2021-12-03 21:40:18 +01:00
throw new UnexpectedValueException('Parser cache provider cannot be null here');
}
$diff_files = [];
2021-12-03 21:25:22 +01:00
$last_run = $this->project_cache_provider->getLastRun(PSALM_VERSION);
foreach ($this->project_files as $file_path) {
if ($this->file_provider->getModifiedTime($file_path) >= $last_run
&& $this->parser_cache_provider->loadExistingFileContentsFromCache($file_path)
!== $this->file_provider->getContents($file_path)
) {
$diff_files[] = $file_path;
}
}
return $diff_files;
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
/**
* @param array<string> $file_list
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
*
*/
private function checkDiffFilesWithConfig(Config $config, array $file_list = []): void
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
{
$files_to_scan = [];
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
foreach ($file_list as $file_path) {
if (!$this->file_provider->fileExists($file_path)) {
continue;
}
if (!$config->isInProjectDirs($file_path)) {
$this->progress->debug('skipping ' . $file_path . "\n");
2017-12-29 11:26:28 -05:00
continue;
}
$files_to_scan[$file_path] = $file_path;
2017-12-29 11:26:28 -05:00
}
2018-02-03 18:52:35 -05:00
$this->codebase->addFilesToAnalyze($files_to_scan);
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
public function checkFile(string $file_path): void
{
$this->progress->debug('Checking ' . $file_path . "\n");
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
$this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
$this->config->hide_external_errors = $this->config->isInProjectDirs($file_path);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
2018-02-03 18:52:35 -05:00
$this->codebase->addFilesToAnalyze([$file_path => $file_path]);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
$this->file_reference_provider->loadReferenceCache();
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
$this->progress->write($this->generatePHPVersionMessage());
$this->progress->startScanningFiles();
$this->config->initializePlugins($this);
$this->codebase->scanFiles($this->threads);
$this->config->visitStubFiles($this->codebase, $this->progress);
$this->progress->startAnalyzingFiles();
2020-04-01 12:56:32 -04:00
$this->codebase->analyzer->analyzeFiles(
$this,
$this->threads,
$this->codebase->alter_code,
$this->codebase->find_unused_code === 'always'
);
}
2017-01-12 00:54:41 -05:00
/**
* @param string[] $paths_to_check
*/
public function checkPaths(array $paths_to_check): void
{
$this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
$this->visitAutoloadFiles();
2020-12-05 15:54:05 +01:00
$this->codebase->scanner->addFilesToShallowScan($this->extra_files);
foreach ($paths_to_check as $path) {
$this->progress->debug('Checking ' . $path . "\n");
if (is_dir($path)) {
$this->checkDirWithConfig($path, $this->config, true);
} elseif (is_file($path)) {
$this->codebase->addFilesToAnalyze([$path => $path]);
$this->config->hide_external_errors = $this->config->isInProjectDirs($path);
}
}
$this->file_reference_provider->loadReferenceCache();
$this->progress->write($this->generatePHPVersionMessage());
$this->progress->startScanningFiles();
$this->config->initializePlugins($this);
$this->codebase->scanFiles($this->threads);
$this->config->visitStubFiles($this->codebase, $this->progress);
$this->progress->startAnalyzingFiles();
2020-04-01 12:56:32 -04:00
$this->codebase->analyzer->analyzeFiles(
$this,
$this->threads,
$this->codebase->alter_code,
$this->codebase->find_unused_code === 'always'
);
if ($this->stdout_report_options
&& in_array(
$this->stdout_report_options->format,
2021-12-03 20:11:20 +01:00
[Report::TYPE_CONSOLE, Report::TYPE_PHP_STORM]
)
&& $this->codebase->collect_references
) {
fwrite(
STDERR,
PHP_EOL . 'To whom it may concern: Psalm cannot detect unused classes, methods and properties'
2019-04-30 13:26:11 -04:00
. PHP_EOL . 'when analyzing individual files and folders. Run on the full project to enable'
. PHP_EOL . 'complete unused code detection.' . PHP_EOL
);
}
}
public function getConfig(): Config
{
return $this->config;
}
2016-10-15 00:12:57 -04:00
/**
* @param array<string> $diff_files
2017-05-26 20:16:18 -04:00
*
2018-10-06 20:11:19 -04:00
* @return array<string, string>
2016-10-15 00:12:57 -04:00
*/
public function getReferencedFilesFromDiff(array $diff_files, bool $include_referencing_files = true): array
{
2016-10-05 17:08:20 -04:00
$all_inherited_files_to_check = $diff_files;
while ($diff_files) {
$diff_file = array_shift($diff_files);
$dependent_files = $this->file_reference_provider->getFilesInheritingFromFile($diff_file);
2016-10-05 17:08:20 -04:00
$new_dependent_files = array_diff($dependent_files, $all_inherited_files_to_check);
$all_inherited_files_to_check = array_merge($all_inherited_files_to_check, $new_dependent_files);
$diff_files = array_merge($diff_files, $new_dependent_files);
2016-10-05 17:08:20 -04:00
}
$all_files_to_check = $all_inherited_files_to_check;
if ($include_referencing_files) {
foreach ($all_inherited_files_to_check as $file_name) {
$dependent_files = $this->file_reference_provider->getFilesReferencingFile($file_name);
$all_files_to_check = array_merge($dependent_files, $all_files_to_check);
}
}
2018-10-06 20:11:19 -04:00
return array_combine($all_files_to_check, $all_files_to_check);
}
2016-12-07 22:38:57 -05:00
public function fileExists(string $file_path): bool
2017-01-19 12:15:42 -05:00
{
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 16:11:02 -04:00
return $this->file_provider->fileExists($file_path);
2017-01-19 12:15:42 -05:00
}
2018-01-07 16:11:51 -05:00
public function alterCodeAfterCompletion(
2020-10-12 21:46:47 +02:00
bool $dry_run = false,
bool $safe_types = false
): void {
2018-11-05 21:57:36 -05:00
$this->codebase->alter_code = true;
$this->codebase->infer_types_from_usage = true;
$this->show_issues = false;
2018-01-07 11:48:33 -05:00
$this->dry_run = $dry_run;
2018-01-07 16:11:51 -05:00
$this->only_replace_php_types_with_non_docblock_types = $safe_types;
}
2018-01-05 13:22:48 -05:00
2019-06-02 01:10:50 -04:00
/**
* @param array<string, string> $to_refactor
2019-06-02 01:10:50 -04:00
*
*/
public function refactorCodeAfterCompletion(array $to_refactor): void
2019-06-02 01:10:50 -04:00
{
$this->to_refactor = $to_refactor;
2019-06-02 01:10:50 -04:00
$this->codebase->alter_code = true;
$this->show_issues = false;
}
/**
* @param 'cli'|'config'|'composer'|'tests' $source
*/
public function setPhpVersion(string $version, string $source): void
{
if (!preg_match('/^(5\.[456]|7\.[01234]|8\.[01])(\..*)?$/', $version)) {
2021-12-03 21:40:18 +01:00
throw new UnexpectedValueException('Expecting a version number in the format x.y');
}
[$php_major_version, $php_minor_version] = explode('.', $version);
Test parallelization (#4045) * Run tests in random order Being able to run tests in any order is a pre-requisite for being able to run them in parallel. * Reset type coverage between tests, fix affected tests * Reset parser and lexer between test runs and on php version change Previously lexer was reset, but parser kept the reference to the old one, and reference to the parser was kept by StatementsProvider. This resulted in order-dependent tests - if the parser was first initialized with phpVersion set to 7.4 then arrow functions worked fine, but were failing when the parser was initially constructed with settings for 7.3 This can be demonstrated on current master by upgrading to nikic/php-parser:4.9 and running: ``` vendor/bin/phpunit --no-coverage --filter="inferredArgArrowFunction" tests/ClosureTest.php ``` Now all tests using PHP 7.4 features must set the PHP version accordingly. * Marked more tests using 7.4 syntax * Reset newline-between-annotation flag between tests * Resolve real paths before passing them to checkPaths When checkPaths is called from psalm.php the paths are resolved, so we just mimicking SUT behaviour here. * Restore newline-between-annotations in DocCommentTest * Tweak Appveyor caches * Tweak TravisCI caches * Tweak CircleCI caches * Run tests in parallel Use `vendor/bin/paratest` instead of `vendor/bin/phpunit` * Use default paratest runner on Windows WrapperRunner is not supported on Windows. * TRAVIS_TAG could be empty * Restore appveyor conditional caching
2020-08-23 17:32:07 +03:00
$php_major_version = (int) $php_major_version;
$php_minor_version = (int) $php_minor_version;
if ($this->codebase->php_major_version !== $php_major_version
|| $this->codebase->php_minor_version !== $php_minor_version
) {
// reset lexer and parser when php version changes
2021-12-03 20:11:20 +01:00
StatementsProvider::clearLexer();
StatementsProvider::clearParser();
Test parallelization (#4045) * Run tests in random order Being able to run tests in any order is a pre-requisite for being able to run them in parallel. * Reset type coverage between tests, fix affected tests * Reset parser and lexer between test runs and on php version change Previously lexer was reset, but parser kept the reference to the old one, and reference to the parser was kept by StatementsProvider. This resulted in order-dependent tests - if the parser was first initialized with phpVersion set to 7.4 then arrow functions worked fine, but were failing when the parser was initially constructed with settings for 7.3 This can be demonstrated on current master by upgrading to nikic/php-parser:4.9 and running: ``` vendor/bin/phpunit --no-coverage --filter="inferredArgArrowFunction" tests/ClosureTest.php ``` Now all tests using PHP 7.4 features must set the PHP version accordingly. * Marked more tests using 7.4 syntax * Reset newline-between-annotation flag between tests * Resolve real paths before passing them to checkPaths When checkPaths is called from psalm.php the paths are resolved, so we just mimicking SUT behaviour here. * Restore newline-between-annotations in DocCommentTest * Tweak Appveyor caches * Tweak TravisCI caches * Tweak CircleCI caches * Run tests in parallel Use `vendor/bin/paratest` instead of `vendor/bin/phpunit` * Use default paratest runner on Windows WrapperRunner is not supported on Windows. * TRAVIS_TAG could be empty * Restore appveyor conditional caching
2020-08-23 17:32:07 +03:00
}
$this->codebase->php_major_version = $php_major_version;
$this->codebase->php_minor_version = $php_minor_version;
$this->codebase->php_version_source = $source;
}
2018-01-05 13:22:48 -05:00
/**
* @param array<string, bool> $issues
* @throws UnsupportedIssueToFixException
*
2018-01-05 13:22:48 -05:00
*/
public function setIssuesToFix(array $issues): void
{
$supported_issues_to_fix = static::getSupportedIssuesToFix();
$supported_issues_to_fix[] = 'MissingImmutableAnnotation';
$supported_issues_to_fix[] = 'MissingPureAnnotation';
$unsupportedIssues = array_diff(array_keys($issues), $supported_issues_to_fix);
if (! empty($unsupportedIssues)) {
throw new UnsupportedIssueToFixException(
'Psalm doesn\'t know how to fix issue(s): ' . implode(', ', $unsupportedIssues) . PHP_EOL
. 'Supported issues to fix are: ' . implode(',', $supported_issues_to_fix)
);
}
$this->issues_to_fix = $issues;
}
public function setAllIssuesToFix(): void
{
$keyed_issues = array_fill_keys(static::getSupportedIssuesToFix(), true);
$this->setIssuesToFix($keyed_issues);
}
/**
* @return array<string, bool>
*/
public function getIssuesToFix(): array
2018-01-05 13:22:48 -05:00
{
return $this->issues_to_fix;
2018-01-05 13:22:48 -05:00
}
2018-01-21 12:44:46 -05:00
public function getCodebase(): Codebase
2018-01-21 12:44:46 -05:00
{
return $this->codebase;
2018-01-21 12:44:46 -05:00
}
public function getFileAnalyzerForClassLike(string $fq_class_name): FileAnalyzer
2018-02-03 18:52:35 -05:00
{
$fq_class_name_lc = strtolower($fq_class_name);
$file_path = $this->codebase->scanner->getClassLikeFilePath($fq_class_name_lc);
2021-09-26 22:01:36 +02:00
return new FileAnalyzer(
2018-02-03 18:52:35 -05:00
$this,
$file_path,
$this->config->shortenFileName($file_path)
);
}
public function getMethodMutations(
2021-12-03 20:11:20 +01:00
MethodIdentifier $original_method_id,
Context $this_context,
string $root_file_path,
string $root_file_name
): void {
$fq_class_name = $original_method_id->fq_class_name;
2018-02-03 18:52:35 -05:00
$appearing_method_id = $this->codebase->methods->getAppearingMethodId($original_method_id);
if (!$appearing_method_id) {
// this can happen for some abstract classes implementing (but not fully) interfaces
return;
2018-01-21 12:44:46 -05:00
}
$appearing_fq_class_name = $appearing_method_id->fq_class_name;
2018-01-21 12:44:46 -05:00
$appearing_class_storage = $this->classlike_storage_provider->get($appearing_fq_class_name);
2018-01-21 12:44:46 -05:00
if (!$appearing_class_storage->user_defined) {
return;
2018-01-21 12:44:46 -05:00
}
$file_analyzer = $this->getFileAnalyzerForClassLike($fq_class_name);
$file_analyzer->setRootFilePath($root_file_path, $root_file_name);
if ($appearing_fq_class_name !== $fq_class_name) {
2018-11-11 12:01:14 -05:00
$file_analyzer = $this->getFileAnalyzerForClassLike($appearing_fq_class_name);
}
2018-10-06 20:11:19 -04:00
$stmts = $this->codebase->getStatementsForFile(
2018-11-11 12:01:14 -05:00
$file_analyzer->getFilePath()
2018-10-06 20:11:19 -04:00
);
2018-11-11 12:01:14 -05:00
$file_analyzer->populateCheckers($stmts);
if (!$this_context->self) {
$this_context->self = $fq_class_name;
$this_context->vars_in_scope['$this'] = Type::parseString($fq_class_name);
}
$file_analyzer->getMethodMutations($appearing_method_id, $this_context, true);
$file_analyzer->class_analyzers_to_analyze = [];
$file_analyzer->interface_analyzers_to_analyze = [];
2019-06-29 21:06:21 -04:00
$file_analyzer->clearSourceBeforeDestruction();
2018-01-21 12:44:46 -05:00
}
public function getFunctionLikeAnalyzer(
2021-12-03 20:11:20 +01:00
MethodIdentifier $method_id,
string $file_path
): ?FunctionLikeAnalyzer {
$file_analyzer = new FileAnalyzer(
$this,
$file_path,
$this->config->shortenFileName($file_path)
);
$stmts = $this->codebase->getStatementsForFile(
$file_analyzer->getFilePath()
);
$file_analyzer->populateCheckers($stmts);
$function_analyzer = $file_analyzer->getFunctionLikeAnalyzer($method_id);
$file_analyzer->class_analyzers_to_analyze = [];
$file_analyzer->interface_analyzers_to_analyze = [];
return $function_analyzer;
}
/**
* Adapted from https://gist.github.com/divinity76/01ef9ca99c111565a72d3a8a6e42f7fb
* returns number of cpu cores
* Copyleft 2018, license: WTFPL
2021-12-04 03:37:19 +01:00
* @throws RuntimeException
* @throws LogicException
* @psalm-suppress ForbiddenCode
*/
2019-06-02 09:59:45 -04:00
public static function getCpuCount(): int
{
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
/*
$str = trim((string) shell_exec('wmic cpu get NumberOfCores 2>&1'));
if (!preg_match('/(\d+)/', $str, $matches)) {
2021-12-04 03:37:19 +01:00
throw new RuntimeException('wmic failed to get number of cpu cores on windows!');
}
return ((int) $matches [1]);
*/
return 1;
}
2021-12-03 21:07:25 +01:00
if (ini_get('pcre.jit') === '1'
2021-12-03 21:25:22 +01:00
&& PHP_OS === 'Darwin'
&& version_compare(PHP_VERSION, '7.3.0') >= 0
&& version_compare(PHP_VERSION, '7.4.0') < 0
2020-02-17 16:33:28 -05:00
) {
return 1;
}
2021-12-03 21:07:25 +01:00
if (!extension_loaded('pcntl') || !function_exists('shell_exec')) {
return 1;
}
$has_nproc = trim((string) @shell_exec('command -v nproc'));
if ($has_nproc) {
$ret = @shell_exec('nproc');
if (is_string($ret)) {
$ret = trim($ret);
$tmp = filter_var($ret, FILTER_VALIDATE_INT);
if (is_int($tmp)) {
return $tmp;
}
}
}
$ret = @shell_exec('sysctl -n hw.ncpu');
if (is_string($ret)) {
$ret = trim($ret);
$tmp = filter_var($ret, FILTER_VALIDATE_INT);
if (is_int($tmp)) {
return $tmp;
}
}
if (is_readable('/proc/cpuinfo')) {
$cpuinfo = file_get_contents('/proc/cpuinfo');
$count = substr_count($cpuinfo, 'processor');
if ($count > 0) {
return $count;
}
}
2021-12-03 21:40:18 +01:00
throw new LogicException('failed to detect number of CPUs!');
}
/**
2020-10-17 18:36:44 +02:00
* @return array<int, string>
2020-08-23 13:52:31 -04:00
*
* @psalm-pure
*/
public static function getSupportedIssuesToFix(): array
{
return array_map(
/** @param class-string $issue_class */
function (string $issue_class): string {
$parts = explode('\\', $issue_class);
return end($parts);
},
self::SUPPORTED_ISSUES_TO_FIX
);
}
2016-06-09 18:08:25 -04:00
}