1
0
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:
Matthew Brown 2016-10-05 13:24:46 -04:00
parent 2261ecf596
commit 3f742ee954
5 changed files with 134 additions and 16 deletions

View File

@ -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)) {

View File

@ -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()

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -66,6 +66,8 @@ class IssueBuffer
public static function finish()
{
Checker\FileChecker::updateReferenceCache();
if (count(self::$errors)) {
exit(1);
}