mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Generate big graph of all files that could possibly be affected
This commit is contained in:
parent
2261ecf596
commit
3f742ee954
@ -19,7 +19,7 @@ ini_set('display_startup_errors', 1);
|
||||
ini_set('memory_limit', '2048M');
|
||||
|
||||
// get options from command line
|
||||
$options = getopt('f:m:', ['debug', 'config:', 'monochrome', 'show-info:']);
|
||||
$options = getopt('f:m:', ['debug', 'config:', 'monochrome', 'show-info:','diff:']);
|
||||
|
||||
// get vars from options
|
||||
$debug = array_key_exists('debug', $options);
|
||||
@ -29,6 +29,7 @@ $use_color = !array_key_exists('monochrome', $options);
|
||||
$show_info = isset($options['show-info'])
|
||||
? $options['show-info'] !== 'false' && $options['show-info'] !== '0'
|
||||
: true;
|
||||
$diff_files = isset($options['diff']) ? explode(',', $options['diff']) : [];
|
||||
|
||||
// set the cache directory for the file checker
|
||||
FileChecker::setCacheDir('/var/tmp/php-parser');
|
||||
@ -44,7 +45,7 @@ ProjectChecker::$show_info = $show_info;
|
||||
$time = microtime(true);
|
||||
|
||||
if ($path_to_check === null) {
|
||||
ProjectChecker::check($debug);
|
||||
ProjectChecker::check($debug, $diff_files);
|
||||
}
|
||||
elseif ($path_to_check) {
|
||||
if (is_dir($path_to_check)) {
|
||||
|
@ -561,7 +561,7 @@ abstract class ClassLikeChecker implements StatementsSource
|
||||
}
|
||||
}
|
||||
|
||||
FileChecker::addFileReferenceToClass($file_name, $absolute_class);
|
||||
FileChecker::addFileReferenceToClass(Config::getInstance()->getBaseDir() . $file_name, $absolute_class);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -682,8 +682,8 @@ abstract class ClassLikeChecker implements StatementsSource
|
||||
|
||||
$short_file_name = $file_checker->getFileName();
|
||||
|
||||
self::$class_files[$class_name] = $short_file_name;
|
||||
self::$file_classes[$short_file_name][] = $class_name;
|
||||
self::$class_files[$class_name] = $class_file_name;
|
||||
self::$file_classes[$class_file_name][] = $class_name;
|
||||
|
||||
// this doesn't work on traits
|
||||
$file_checker->check(true, false);
|
||||
@ -910,7 +910,7 @@ abstract class ClassLikeChecker implements StatementsSource
|
||||
|
||||
public static function getClassesForFile($file_name)
|
||||
{
|
||||
return array_unique(self::$file_classes[$file_name]);
|
||||
return isset(self::$file_classes[$file_name]) ? array_unique(self::$file_classes[$file_name]) : [];
|
||||
}
|
||||
|
||||
public static function clearCache()
|
||||
|
@ -36,10 +36,12 @@ class FileChecker implements StatementsSource
|
||||
|
||||
protected static $functions_checked = [];
|
||||
protected static $classes_checked = [];
|
||||
protected static $file_checked = [];
|
||||
protected static $files_checked = [];
|
||||
|
||||
public static $show_notices = true;
|
||||
|
||||
const REFERENCE_CACHE_NAME = 'references';
|
||||
|
||||
/**
|
||||
* A lookup table used for getting all the files that reference a class
|
||||
*
|
||||
@ -47,6 +49,20 @@ class FileChecker implements StatementsSource
|
||||
*/
|
||||
protected static $file_references_to_class = [];
|
||||
|
||||
/**
|
||||
* A lookup table used for getting all the files referenced by a file
|
||||
*
|
||||
* @var array<string,array<string,bool>>
|
||||
*/
|
||||
protected static $file_references = [];
|
||||
|
||||
/**
|
||||
* A lookup table used for getting all the files that reference any other file
|
||||
*
|
||||
* @var array<string,array<string,bool>>
|
||||
*/
|
||||
protected static $referencing_files = [];
|
||||
|
||||
public function __construct($file_name, array $preloaded_statements = [])
|
||||
{
|
||||
$this->real_file_name = $file_name;
|
||||
@ -62,7 +78,7 @@ class FileChecker implements StatementsSource
|
||||
|
||||
public function check($check_classes = true, $check_functions = true, Context $file_context = null, $cache = true)
|
||||
{
|
||||
if ($cache && isset(self::$functions_checked[$this->real_file_name])) {
|
||||
if ($cache && isset(self::$functions_checked[$this->short_file_name])) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -70,7 +86,7 @@ class FileChecker implements StatementsSource
|
||||
return;
|
||||
}
|
||||
|
||||
if ($cache && !$check_classes && !$check_functions && isset(self::$file_checked[$this->real_file_name])) {
|
||||
if ($cache && !$check_classes && !$check_functions && isset(self::$files_checked[$this->real_file_name])) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -162,7 +178,7 @@ class FileChecker implements StatementsSource
|
||||
self::$classes_checked[$this->real_file_name] = true;
|
||||
}
|
||||
|
||||
self::$file_checked[$this->real_file_name] = true;
|
||||
self::$files_checked[$this->real_file_name] = true;
|
||||
|
||||
return $stmts;
|
||||
}
|
||||
@ -267,6 +283,31 @@ class FileChecker implements StatementsSource
|
||||
return $stmts;
|
||||
}
|
||||
|
||||
public static function loadReferenceCache()
|
||||
{
|
||||
if (self::$cache_dir) {
|
||||
$cache_location = self::$cache_dir . '/' . self::REFERENCE_CACHE_NAME;
|
||||
|
||||
if (is_readable($cache_location)) {
|
||||
self::$file_references = unserialize((string) file_get_contents($cache_location));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function updateReferenceCache()
|
||||
{
|
||||
if (self::$cache_dir) {
|
||||
$cache_location = self::$cache_dir . '/' . self::REFERENCE_CACHE_NAME;
|
||||
|
||||
foreach (self::$files_checked as $file => $_) {
|
||||
$existing_references_to_file = isset(self::$file_references[$file]) ? self::$file_references[$file] : [];
|
||||
self::$file_references[$file] = array_unique(array_merge($existing_references_to_file, self::calculateFilesReferencingFile($file)));
|
||||
}
|
||||
|
||||
file_put_contents($cache_location, serialize(self::$file_references));
|
||||
}
|
||||
}
|
||||
|
||||
public static function setCacheDir($cache_dir)
|
||||
{
|
||||
self::$cache_dir = $cache_dir;
|
||||
@ -323,6 +364,11 @@ class FileChecker implements StatementsSource
|
||||
return $this->short_file_name;
|
||||
}
|
||||
|
||||
public function getRealFileName()
|
||||
{
|
||||
return $this->real_file_name;
|
||||
}
|
||||
|
||||
public function getIncludeFileName()
|
||||
{
|
||||
return $this->include_file_name;
|
||||
@ -379,10 +425,11 @@ class FileChecker implements StatementsSource
|
||||
|
||||
public static function addFileReferenceToClass($source_file, $absolute_class)
|
||||
{
|
||||
self::$referencing_files[$source_file] = true;
|
||||
self::$file_references_to_class[$absolute_class][$source_file] = true;
|
||||
}
|
||||
|
||||
public static function getFilesReferencingFile($file)
|
||||
public static function calculateFilesReferencingFile($file)
|
||||
{
|
||||
$referenced_files = [];
|
||||
|
||||
@ -397,13 +444,18 @@ class FileChecker implements StatementsSource
|
||||
return array_unique($referenced_files);
|
||||
}
|
||||
|
||||
public static function getFilesReferencingFile($file)
|
||||
{
|
||||
return isset(self::$file_references[$file]) ? self::$file_references[$file] : [];
|
||||
}
|
||||
|
||||
public static function clearCache()
|
||||
{
|
||||
self::$file_checkers = [];
|
||||
|
||||
self::$functions_checked = [];
|
||||
self::$classes_checked = [];
|
||||
self::$file_checked = [];
|
||||
self::$files_checked = [];
|
||||
|
||||
ClassLikeChecker::clearCache();
|
||||
}
|
||||
|
@ -29,14 +29,23 @@ class ProjectChecker
|
||||
*/
|
||||
public static $show_info = true;
|
||||
|
||||
public static function check($debug = false)
|
||||
public static function check($debug = false, array $diff_files = [])
|
||||
{
|
||||
if (!self::$config) {
|
||||
self::$config = self::getConfigForPath(getcwd());
|
||||
}
|
||||
|
||||
foreach (self::$config->getIncludeDirs() as $dir_name) {
|
||||
self::checkDirWithConfig($dir_name, self::$config, $debug);
|
||||
$file_list = [];
|
||||
|
||||
if ($diff_files) {
|
||||
FileChecker::loadReferenceCache();
|
||||
$file_list = self::getReferencedFilesFromDiff($diff_files);
|
||||
self::checkDiffFilesWithConfig($file_list, self::$config, $debug);
|
||||
}
|
||||
else {
|
||||
foreach (self::$config->getIncludeDirs() as $dir_name) {
|
||||
self::checkDirWithConfig($dir_name, self::$config, $debug, $file_list);
|
||||
}
|
||||
}
|
||||
|
||||
IssueBuffer::finish();
|
||||
@ -49,12 +58,14 @@ class ProjectChecker
|
||||
self::$config->hide_external_errors = self::$config->isInProjectDirs(self::$config->shortenFileName($dir_name));
|
||||
}
|
||||
|
||||
FileChecker::loadReferenceCache();
|
||||
|
||||
self::checkDirWithConfig($dir_name, self::$config, $debug);
|
||||
|
||||
IssueBuffer::finish();
|
||||
}
|
||||
|
||||
protected static function checkDirWithConfig($dir_name, Config $config, $debug)
|
||||
protected static function checkDirWithConfig($dir_name, Config $config, $debug, array $file_list = [])
|
||||
{
|
||||
$file_extensions = $config->getFileExtensions();
|
||||
$filetype_handlers = $config->getFiletypeHandlers();
|
||||
@ -75,6 +86,15 @@ class ProjectChecker
|
||||
echo 'Checking ' . $file_name . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($file_list) {
|
||||
if (in_array($file_name, $file_list)) {
|
||||
echo 'Checking affected file ' . $file_name . PHP_EOL;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($filetype_handlers[$extension])) {
|
||||
/** @var FileChecker */
|
||||
$file_checker = new $filetype_handlers[$extension]($file_name);
|
||||
@ -91,6 +111,30 @@ class ProjectChecker
|
||||
}
|
||||
}
|
||||
|
||||
protected static function checkDiffFilesWithConfig(array $file_list = [], Config $config, $debug)
|
||||
{
|
||||
$file_extensions = $config->getFileExtensions();
|
||||
$filetype_handlers = $config->getFiletypeHandlers();
|
||||
|
||||
foreach ($file_list as $file_name) {
|
||||
$extension = pathinfo($file_name, PATHINFO_EXTENSION);
|
||||
|
||||
if ($debug) {
|
||||
echo 'Checking affected file ' . $file_name . PHP_EOL;
|
||||
}
|
||||
|
||||
if (isset($filetype_handlers[$extension])) {
|
||||
/** @var FileChecker */
|
||||
$file_checker = new $filetype_handlers[$extension]($file_name);
|
||||
}
|
||||
else {
|
||||
$file_checker = new FileChecker($file_name);
|
||||
}
|
||||
|
||||
$file_checker->check(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkFile($file_name, $debug = false)
|
||||
{
|
||||
if ($debug) {
|
||||
@ -109,6 +153,8 @@ class ProjectChecker
|
||||
|
||||
$filetype_handlers = self::$config->getFiletypeHandlers();
|
||||
|
||||
FileChecker::loadReferenceCache();
|
||||
|
||||
if (isset($filetype_handlers[$extension])) {
|
||||
/** @var FileChecker */
|
||||
$file_checker = new $filetype_handlers[$extension]($file_name);
|
||||
@ -177,4 +223,21 @@ class ProjectChecker
|
||||
require_once($dir_path . self::$config->autoloader);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getReferencedFilesFromDiff(array $diff_files)
|
||||
{
|
||||
$all_files_to_check = $diff_files;
|
||||
|
||||
while ($diff_files) {
|
||||
$diff_file = array_shift($diff_files);
|
||||
$dependent_files = FileChecker::getFilesReferencingFile($diff_file);
|
||||
//var_dump($dependent_files);
|
||||
$new_files = array_diff($dependent_files, $all_files_to_check);
|
||||
|
||||
$all_files_to_check += $new_files;
|
||||
$diff_files += $new_files;
|
||||
}
|
||||
|
||||
return $all_files_to_check;
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,8 @@ class IssueBuffer
|
||||
|
||||
public static function finish()
|
||||
{
|
||||
Checker\FileChecker::updateReferenceCache();
|
||||
|
||||
if (count(self::$errors)) {
|
||||
exit(1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user