Skip to content

Commit

Permalink
Improve loading of stub file and autoload file information
Browse files Browse the repository at this point in the history
Ref #849
  • Loading branch information
muglug committed Jun 30, 2018
1 parent dbde36b commit 16e270f
Show file tree
Hide file tree
Showing 16 changed files with 707 additions and 422 deletions.
17 changes: 14 additions & 3 deletions src/Psalm/Checker/ProjectChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,6 @@ public function __construct(
self::$instance = $this;

$this->cache_provider->use_igbinary = $config->use_igbinary;

$config->visitStubFiles($this->codebase);
$config->initializePlugins($this);
}

/**
Expand Down Expand Up @@ -283,6 +280,8 @@ public function check($base_dir, $is_diff = false)
$this->codebase->addFilesToAnalyze([$file_path => $file_path]);
}

$this->config->initializePlugins($this);

$this->codebase->scanFiles();
} else {
if ($this->debug_output) {
Expand All @@ -297,6 +296,8 @@ public function check($base_dir, $is_diff = false)

$this->checkDiffFilesWithConfig($this->config, $file_list);

$this->config->initializePlugins($this);

$this->codebase->scanFiles();
}
}
Expand All @@ -305,6 +306,8 @@ public function check($base_dir, $is_diff = false)
echo 'Analyzing files...' . "\n";
}

$this->config->visitStubFiles($this->codebase, $this->debug_output);

$this->codebase->analyzer->analyzeFiles($this, $this->threads, $this->alter_code);

$removed_parser_files = $this->cache_provider->deleteOldParserCaches(
Expand Down Expand Up @@ -386,8 +389,12 @@ public function checkDir($dir_name)
echo 'Scanning files...' . "\n";
}

$this->config->initializePlugins($this);

$this->codebase->scanFiles();

$this->config->visitStubFiles($this->codebase, $this->debug_output);

if ($this->output_format === self::TYPE_CONSOLE) {
echo 'Analyzing files...' . "\n";
}
Expand Down Expand Up @@ -547,8 +554,12 @@ public function checkFile($file_path)
echo 'Scanning files...' . "\n";
}

$this->config->initializePlugins($this);

$this->codebase->scanFiles();

$this->config->visitStubFiles($this->codebase, $this->debug_output);

if ($this->output_format === self::TYPE_CONSOLE) {
echo 'Analyzing files...' . "\n";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,12 @@ public static function getArrayAccessTypeGivenOffset(

if ($type instanceof TNamedObject) {
if (strtolower($type->value) !== 'simplexmlelement'
&& $codebase->classExists($type->value)
&& !$codebase->classImplements($type->value, 'ArrayAccess')
&& strtolower($type->value) !== 'arrayaccess'
&& (($codebase->classExists($type->value)
&& !$codebase->classImplements($type->value, 'ArrayAccess'))
|| ($codebase->interfaceExists($type->value)
&& !$codebase->interfaceExtends($type->value, 'ArrayAccess'))
)
) {
$non_array_types[] = (string)$type;
} else {
Expand Down
4 changes: 3 additions & 1 deletion src/Psalm/Checker/StatementsChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,9 @@ function ($line) {
}
}

if (!$project_checker->codebase->register_global_functions) {
if (!$project_checker->codebase->register_stub_files
&& !$project_checker->codebase->register_autoload_files
) {
$function_id = strtolower($stmt->name->name);
$function_context = new Context($context->self);
$config = Config::getInstance();
Expand Down
11 changes: 9 additions & 2 deletions src/Psalm/Codebase.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,19 @@ class Codebase
*/
private static $stubbed_constants = [];

/**
* Whether to register autoloaded information
*
* @var bool
*/
public $register_autoload_files = false;

/**
* Whether to log functions just at the file level or globally (for stubs)
*
* @var bool
*/
public $register_global_functions = false;
public $register_stub_files = false;

/**
* @var bool
Expand Down Expand Up @@ -404,7 +411,7 @@ public function getClosureStorage($file_path, $closure_id)
*
* @return void
*/
public function addStubbedConstantType($const_id, $type)
public function addGlobalConstantType($const_id, Type\Union $type)
{
self::$stubbed_constants[$const_id] = $type;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Codebase/Functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public function getStorage($statements_checker, $function_id)
*
* @return void
*/
public function addStubbedFunction($function_id, FunctionLikeStorage $storage)
public function addGlobalFunction($function_id, FunctionLikeStorage $storage)
{
self::$stubbed_functions[strtolower($function_id)] = $storage;
}
Expand Down
8 changes: 6 additions & 2 deletions src/Psalm/Codebase/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,13 @@ private function scanFile(
$this->queueClassLikeForScanning($fq_classlike_name, $file_path, false, false);
}

if ($this->codebase->register_global_functions) {
if ($this->codebase->register_autoload_files) {
foreach ($file_storage->functions as $function_storage) {
$this->codebase->functions->addStubbedFunction($function_storage->cased_name, $function_storage);
$this->codebase->functions->addGlobalFunction($function_storage->cased_name, $function_storage);
}

foreach ($file_storage->constants as $name => $type) {
$this->codebase->addGlobalConstantType($name, $type);
}
}
}
Expand Down
45 changes: 31 additions & 14 deletions src/Psalm/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -1010,11 +1010,13 @@ public function getMockClasses()
}

/**
* @param bool $debug
*
* @return void
*/
public function visitStubFiles(Codebase $codebase)
public function visitStubFiles(Codebase $codebase, $debug = false)
{
$codebase->register_global_functions = true;
$codebase->register_stub_files = true;

// note: don't realpath $generic_stubs_path, or phar version will fail
$generic_stubs_path = __DIR__ . '/Stubs/CoreGenericFunctions.php';
Expand All @@ -1032,16 +1034,21 @@ public function visitStubFiles(Codebase $codebase)

$stub_files = array_merge([$generic_stubs_path, $generic_classes_path], $this->stub_files);

foreach ($stub_files as $stub_file_path) {
$file_storage = $codebase->createFileStorageForPath($stub_file_path);
$file_to_scan = new FileScanner($stub_file_path, $this->shortenFileName($stub_file_path), false);
$file_to_scan->scan(
$codebase,
$file_storage
);
foreach ($stub_files as $file_path) {
$codebase->scanner->addFileToShallowScan($file_path);
}

if ($debug) {
echo 'Registering stub files' . "\n";
}

$codebase->scanFiles();

if ($debug) {
echo 'Finished registering stub files' . "\n";
}

$codebase->register_global_functions = false;
$codebase->register_stub_files = false;
}

/**
Expand Down Expand Up @@ -1101,12 +1108,14 @@ public function collectPredefinedFunctions()
}

/**
* @param bool $debug
*
* @return void
*
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
*/
public function visitComposerAutoloadFiles(ProjectChecker $project_checker)
public function visitComposerAutoloadFiles(ProjectChecker $project_checker, $debug = false)
{
$composer_json_path = $this->base_dir . 'composer.json'; // this should ideally not be hardcoded

Expand Down Expand Up @@ -1152,15 +1161,23 @@ public function visitComposerAutoloadFiles(ProjectChecker $project_checker)

if ($autoload_files_files) {
$codebase = $project_checker->codebase;
$codebase->register_global_functions = true;
$codebase->register_autoload_files = true;

foreach ($autoload_files_files as $file_path) {
$codebase->scanner->addFileToShallowScan($file_path);
$codebase->scanner->addFileToDeepScan($file_path);
}

if ($debug) {
echo 'Registering autoloaded files' . "\n";
}

$codebase->scanner->scanFiles($codebase->classlikes);

$project_checker->codebase->register_global_functions = false;
if ($debug) {
echo 'Finished registering autoloaded files' . "\n";
}

$project_checker->codebase->register_autoload_files = false;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/Psalm/Scanner/FileScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function scan(
if ((!$this->will_analyze || $file_storage->deep_scan)
&& $storage_from_cache
&& !$file_storage->has_trait
&& !$codebase->register_stub_files
) {
return;
}
Expand Down
92 changes: 56 additions & 36 deletions src/Psalm/Visitor/DependencyFinderVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ public function enterNode(PhpParser\Node $node)
} elseif ($node instanceof PhpParser\Node\Stmt\ClassLike) {
$class_location = new CodeLocation($this->file_scanner, $node, null, true);

$storage = null;

if ($node->name === null) {
if (!$node instanceof PhpParser\Node\Stmt\Class_) {
throw new \LogicException('Anonymous classes are always classes');
Expand All @@ -167,34 +169,34 @@ public function enterNode(PhpParser\Node $node)
$fq_classlike_name =
($this->aliases->namespace ? $this->aliases->namespace . '\\' : '') . $node->name->name;

if ($this->codebase->classlike_storage_provider->has($fq_classlike_name)
&& !$this->codebase->register_global_functions
) {
$other_file_path = null;

try {
$other_file_path = $this->codebase->scanner->getClassLikeFilePath($fq_classlike_name);
} catch (\UnexpectedValueException $e) {
// do nothing
}

$duplicate_class_storage = $this->codebase->classlike_storage_provider->get($fq_classlike_name);
if ($this->codebase->classlike_storage_provider->has($fq_classlike_name)) {
$duplicate_storage = $this->codebase->classlike_storage_provider->get($fq_classlike_name);

if (!$this->codebase->register_stub_files) {
if (!$duplicate_storage->location
|| $duplicate_storage->location->file_path !== $this->file_path
|| $class_location->getHash() !== $duplicate_storage->location->getHash()
) {
if (IssueBuffer::accepts(
new DuplicateClass(
'Class ' . $fq_classlike_name . ' has already been defined'
. ($duplicate_storage->location
? ' in ' . $duplicate_storage->location->file_path
: ''),
new CodeLocation($this->file_scanner, $node, null, true)
)
)) {
$this->file_storage->has_visitor_issues = true;
}

if (!$duplicate_class_storage->location
|| $duplicate_class_storage->location->file_path !== $this->file_path
|| $class_location->getHash() !== $duplicate_class_storage->location->getHash()
) {
if (IssueBuffer::accepts(
new DuplicateClass(
'Class ' . $fq_classlike_name . ' has already been defined'
. ($other_file_path ? ' in ' . $other_file_path : ''),
new CodeLocation($this->file_scanner, $node, null, true)
)
)) {
$this->file_storage->has_visitor_issues = true;
return PhpParser\NodeTraverser::STOP_TRAVERSAL;
}

return PhpParser\NodeTraverser::STOP_TRAVERSAL;
} elseif ($duplicate_storage->location
&& ($duplicate_storage->location->file_path !== $this->file_path
|| $class_location->getHash() !== $duplicate_storage->location->getHash())
) {
// we're overwriting some methods
$storage = $duplicate_storage;
}
}
}
Expand All @@ -205,11 +207,13 @@ public function enterNode(PhpParser\Node $node)

$this->fq_classlike_names[] = $fq_classlike_name;

$storage = $this->codebase->createClassLikeStorage($fq_classlike_name);
if (!$storage) {
$storage = $this->codebase->createClassLikeStorage($fq_classlike_name);
}

$storage->location = $class_location;
$storage->user_defined = !$this->codebase->register_global_functions;
$storage->stubbed = $this->codebase->register_global_functions;
$storage->user_defined = !$this->codebase->register_stub_files;
$storage->stubbed = $this->codebase->register_stub_files;

$doc_comment = $node->getDocComment();

Expand Down Expand Up @@ -553,14 +557,14 @@ public function enterNode(PhpParser\Node $node)
$const_type = StatementsChecker::getSimpleType($this->codebase, $const->value, $this->aliases)
?: Type::getMixed();

if ($this->codebase->register_global_functions) {
$this->codebase->addStubbedConstantType($const->name->name, $const_type);
if ($this->codebase->register_stub_files || $this->codebase->register_autoload_files) {
$this->codebase->addGlobalConstantType($const->name->name, $const_type);
}

$this->file_storage->constants[$const->name->name] = $const_type;
$this->file_storage->declaring_constants[$const->name->name] = $this->file_path;
}
} elseif ($this->codebase->register_global_functions && $node instanceof PhpParser\Node\Stmt\If_) {
} elseif ($this->codebase->register_autoload_files && $node instanceof PhpParser\Node\Stmt\If_) {
if ($node->cond instanceof PhpParser\Node\Expr\BooleanNot) {
if ($node->cond->expr instanceof PhpParser\Node\Expr\FuncCall
&& $node->cond->expr->name instanceof PhpParser\Node\Name
Expand Down Expand Up @@ -689,13 +693,20 @@ private function registerFunctionLike(PhpParser\Node\FunctionLike $stmt, $fake_m
$function_id = strtolower($cased_function_id);

if (isset($this->file_storage->functions[$function_id])) {
if ($this->codebase->register_stub_files || $this->codebase->register_autoload_files) {
$this->codebase->functions->addGlobalFunction(
$function_id,
$this->file_storage->functions[$function_id]
);
}

return $this->file_storage->functions[$function_id];
}

$storage = new FunctionLikeStorage();

if ($this->codebase->register_global_functions) {
$this->codebase->functions->addStubbedFunction($function_id, $storage);
if ($this->codebase->register_stub_files || $this->codebase->register_autoload_files) {
$this->codebase->functions->addGlobalFunction($function_id, $storage);
}

$this->file_storage->functions[$function_id] = $storage;
Expand All @@ -716,11 +727,19 @@ private function registerFunctionLike(PhpParser\Node\FunctionLike $stmt, $fake_m

$class_storage = $this->classlike_storages[count($this->classlike_storages) - 1];

$storage = null;

if (isset($class_storage->methods[strtolower($stmt->name->name)])) {
throw new \InvalidArgumentException('Cannot re-register ' . $function_id);
if (!$this->codebase->register_stub_files) {
throw new \InvalidArgumentException('Cannot re-register ' . $function_id);
}

$storage = $class_storage->methods[strtolower($stmt->name->name)];
}

$storage = $class_storage->methods[strtolower($stmt->name->name)] = new MethodStorage();
if (!$storage) {
$storage = $class_storage->methods[strtolower($stmt->name->name)] = new MethodStorage();
}

$class_name_parts = explode('\\', $fq_classlike_name);
$class_name = array_pop($class_name_parts);
Expand Down Expand Up @@ -786,6 +805,7 @@ private function registerFunctionLike(PhpParser\Node\FunctionLike $stmt, $fake_m
$has_optional_param = false;

$existing_params = [];
$storage->params = [];

/** @var PhpParser\Node\Param $param */
foreach ($stmt->getParams() as $param) {
Expand Down
Loading

0 comments on commit 16e270f

Please sign in to comment.