Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Dynamic class resolution at execution time, using Composer #1586

Open
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

oligriffiths
Copy link

With a large codebase, the analyze step takes a long time, even with parallelization.

This PR introduces the concept of a ClassResolver the optionally resolve a class name to a file, and analyze the file at execution time rather, then rather than analyzing the entire codebase, only analyze files as they are required, like how autoloading works in PHP.

I've added 2 class resolvers to begin with, a ComposerResolver and an ReflectionResolver. The composer one directly uses the composer autoloader, and the reflection resolver uses phps native autoload mechanism and uses reflection to find a classes file (less efficient).

@oligriffiths oligriffiths changed the title Allow autoloading of Classes using Composer Dynamic class resolution at execution time, using Composer Mar 19, 2018
Copy link
Member

@TysonAndre TysonAndre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still has build failures to resolve. It looks like it's running out of memory, possibly due to infinite recursion. Enabling XDebug and the corresponding ENV Settings would let you know more

Also, I only skimmed this: Is this resulting in skipping the parse phase?

OK (835 tests, 2002 assertions)
Running test suite: __FakeSelfTest
PHP Fatal error:  Allowed memory size of 1073741824 bytes exhausted (tried to allocate 20480 bytes) in /home/travis/build/phan/phan/src/Phan/AST/Parser.php on line 41
Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 20480 bytes) in /home/travis/build/phan/phan/src/Phan/AST/Parser.php on line 41
Running test suite: __FakeSelfFallbackTest
PHP Fatal error:  Allowed memory size of 1073741824 bytes exhausted (tried to allocate 67108864 bytes) in /home/travis/build/phan/phan/vendor/microsoft/tolerant-php-parser/src/PhpTokenizer.php on line 111
Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 67108864 bytes) in /home/travis/build/phan/phan/vendor/microsoft/tolerant-php-parser/src/PhpTokenizer.php on line 111

src/codebase.php Outdated
} else {
// This is the path to autoload.php when Phan is installed globally.
require_once __DIR__ . '/../../../autoload.php';
$autoloader = require_once __DIR__ . '/../../../autoload.php';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When phan is installed globally, it would point to Phan's vendor/ directory, not the project directory.

not your code: Also, I think the line comments might be switched?

@@ -753,14 +766,64 @@ private function resolveClassAliasesForAliasSet(FullyQualifiedClassName $origina
* True if a Clazz with the given FQSEN exists
*/
public function hasClassWithFQSEN(
FullyQualifiedClassName $fqsen
FullyQualifiedClassName $fqsen,
bool $autoload = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new feature, and should be disabled by default. We may enable it in a subsequent backwards incompatible release.

Also, check that the autoloader is actually the autoloader of the project being analyzed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a file is directly analyzed, like doing phan path/to/file.php then this https://github.com/phan/phan/pull/1586/files#diff-f9e2de20caef1fe6f6719d13f0f6c7cbR324 is executed. This is the explicit case where we do not want to do autoloading as that visitor subsequently adds the class in question to the codebase anyway, for all other use cases like finding a class in a property docblock or a method signature, we do want autoloading.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new feature, and should be disabled by default.

Still needs to be addressed. E.g. add a new option --use-project-composer-autoloader in src/Phan/CLI.php (see other comment in codebase.php).

Could you point me at a small example project and .phan/config.php and composer.json as an example of what you're referring to? (I'm assuming the exact command used would be phan path/to/file.php) I'm not clear on what you mean.

  • Is there a directory/file list for .phan/config.php in your use case?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I'm assuming the exact command used would be phan path/to/file.php) I'm not clear on what you mean.

yeah that's exactly right (or a set of files). In this case, there would be no explicit directory/file list, or it would be skipped (as I believe is the case when you path directly to a file)

$this->flushDependenciesForFile($file_path);

// Parse the file
$context = Analysis::parseFile($this, $file_path);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a check that this didn't already parse the file in question -- Some projects have inaccurate composer autoloading info, (E.g. for PSR unoptimized loading), and the file may not actually contain the class composer would expect it to have.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i need to check this for sure.


// Parse the file
$context = Analysis::parseFile($this, $file_path);
$this->setCurrentParsedFile(null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set the parsed file to what it was before the autoloading started.

Also, if we already started parsing a file, this would need to check that we don't re-enter parsing

(E.g. src/b.php references class NS\B, but doesn't declare NS\B, so the parser would try to autoload NS\B, overflowing the stack

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, good call

if (!file_exists($file) || !$file_path) {
throw new FileNotFoundException(
sprintf(
'The path returned from the class resolver for the classs "%s" does not exist: "%s"',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: class, not classs

@TysonAndre
Copy link
Member

TysonAndre commented Mar 20, 2018

This PR is making Phan recursively call itself for src/Phan/AST/ASTSimplifier.php. See my earlier comments (It's also possible to have a cycle of files referring to each other (A->B->A). XDebug will help you debug and analyze this, see the below command and https://xdebug.org/docs/display

Also, if we already started parsing a file, this would need to check that we don't re-enter parsing.

You can use this command to investigate the failures. (With xdebug 2.6.0)

PHAN_ALLOW_XDEBUG=1 php -d xdebug.collect_params=3 -d xdebug.var_display_max_depth=1 -d xdebug.show_error_trace -d zend_extension=xdebug.so ./phan 2>&1 | tee errors.txt

The trace of that error is mentioned in this gist: https://gist.github.com/TysonAndre/7ad71b5a9b9d27272930a910b736c725 . It looks like the below:

    0.2069   17797232  49. Phan\Parse\ParseVisitor->visitClass(class ast\Node { public $kind = 69; public $flags = 0; public $lineno = 11; public $children = array (...); public $endLineno = 530 }) /path/to/phan/src/Phan/Analysis.php:184
    0.2069   17797232  50. Phan\CodeBase->hasClassWithFQSEN(class Phan\Language\FQSEN\FullyQualifiedClassName { private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}namespace = '\\Phan\\AST'; private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}asString = NULL; private ${Phan\Language\FQSEN\AbstractFQSEN}name = 'ASTSimplifier'; protected $alternate_id = 0; private $memoized_data = array (...); private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}memoized_data = array (...) }, ???) /path/to/phan/src/Phan/Parse/ParseVisitor.php:109
    0.2082   18625112  56. Phan\CodeBase->hasClassWithFQSEN(class Phan\Language\FQSEN\FullyQualifiedClassName { private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}namespace = '\\Phan\\AST'; private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}asString = NULL; private ${Phan\Language\FQSEN\AbstractFQSEN}name = 'ASTSimplifier'; protected $alternate_id = 0; private $memoized_data = array (...); private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}memoized_data = array (...) }, ???) /path/to/phan/src/Phan/Parse/ParseVisitor.php:109

    0.2082   18625112  57. Phan\CodeBase->lazyLoadClassWithFQSEN(class Phan\Language\FQSEN\FullyQualifiedClassName { private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}namespace = '\\Phan\\AST'; private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}asString = NULL; private ${Phan\Language\FQSEN\AbstractFQSEN}name = 'ASTSimplifier'; protected $alternate_id = 0; private $memoized_data = array (...); private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}memoized_data = array (...) }) /path/to/phan/src/Phan/CodeBase.php:776
    0.2082   18625336  58. Phan\Analysis::parseFile(class Phan\CodeBase { private $fqsen_class_map = class Phan\Library\Map { ... }; private $fqsen_class_map_user_defined = class Phan\Library\Map { ... }; private $fqsen_class_map_internal = class Phan\Library\Map { ... }; private $fqsen_class_map_reflection = class Phan\Library\Map { ... }; private $fqsen_alias_map = class Phan\Library\Map { ... }; private $fqsen_global_constant_map = class Phan\Library\Map { ... }; private $fqsen_func_map = class Phan\Library\Map { ... }; private $internal_function_fqsen_set = class Phan\Library\Set { ... }; private $method_set = class Phan\Library\Set { ... }; private $class_fqsen_class_map_map = class Phan\Library\Map { ... }; private $name_method_map = array (...); private $parsed_namespace_maps = array (...); private $file_level_suppression_set = array (...); private $should_hydrate_requested_elements = FALSE; private $undo_tracker = NULL; private $has_enabled_undo_tracker = FALSE; private $class_resolver = class Phan\ClassResolver\ComposerResolver { ... } }, 'src/Phan/AST/ASTSimplifier.php', ???, ???, ???) /path/to/phan/src/Phan/CodeBase.php:822

    0.2093   19452992  61. Phan\Parse\ParseVisitor->visitClass(class ast\Node { public $kind = 69; public $flags = 0; public $lineno = 11; public $children = array (...); public $endLineno = 530 }) /path/to/phan/src/Phan/Analysis.php:184
    0.2093   19452992  62. Phan\CodeBase->hasClassWithFQSEN(class Phan\Language\FQSEN\FullyQualifiedClassName { private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}namespace = '\\Phan\\AST'; private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}asString = NULL; private ${Phan\Language\FQSEN\AbstractFQSEN}name = 'ASTSimplifier'; protected $alternate_id = 0; private $memoized_data = array (...); private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}memoized_data = array (...) }, ???) /path/to/phan/src/Phan/Parse/ParseVisitor.php:109

Re: Gitter question:

Phan normalizes namespaces and class names to lowercase to create a cache key (But not the FQSEN, as far as I remember). The first time it encounters an FQSEN, it uses that capitalization for the remainder of Phan's execution (Even in daemon mode)

  • So, if the phpdoc has the wrong casing, it might fail composer autoloading. I'm not sure if that's what you're seeing
  • Function names are also lowercased for class keys
  • Even if that class name is in a doc comment.
  • For class constants, the name is case sensitive (And if I remember correctly, the namespace is not)

The calls I'm seeing to hasClassWithFQSEN via xdebug have the casing one would expect, and aren't cast to lowercase

private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}namespace = '\\Phan\\AST'; private ${Phan\Language\FQSEN\FullyQualifiedGlobalStructuralElement}asString = NULL; private ${Phan\Language\FQSEN\AbstractFQSEN}name = 'ASTSimplifier';

@oligriffiths
Copy link
Author

@TysonAndre thanks for the feedback, will do some debugging.

RE the FQSEN capitalization, I confirmed that the method signature is using the correct casing, the issue is specifically caused by the lowercasing in the code, https://github.com/phan/phan/pull/720/files?diff=unified#diff-2ab884aa2002a82e22d7e4ca67efbc1bR136 and https://github.com/phan/phan/pull/720/files?diff=unified#diff-2ab884aa2002a82e22d7e4ca67efbc1bR159 this causes the FullyQualifiedClassName to be created with the lowercased class name, meaning I can't autoload it.


// Parse the file
$context = Analysis::parseFile($this, $file_path);
unset($this->class_resolver_parsing[$class]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code may still parse the same file multiple times.

Phan should only parse a file exactly once - It may end up with duplicate/alternate function/closure definitions, which may result in misleading error messages.

src/codebase.php Outdated
@@ -37,10 +37,19 @@
$handler->check();
}

// Attempt to load a composer class loader
$class_resolver = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This is immediately overwritten in both possible branches, remove this line

src/codebase.php Outdated
// Attempt to load a composer class loader
$class_resolver = null;
if ($autoloader instanceof \Composer\Autoload\ClassLoader) {
$class_resolver = new \Phan\ClassResolver\ComposerResolver($autoloader);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Phan is also distributed as a phar file. It can also be installed on its own and used to analyze a completely separate directory.

In order to be broadly useful, that would have to load composer from the other project (autoload_static.php)

  • autoload_static seems to be used by php 5.6+, excluding HHVM
  • The autoloader can be created by create a (potentially) separate ClassLoader instance for the project being analyzed, then create an initializer, then invoke that initializer ( public static function getInitializer(ClassLoader $loader) in autoload_static.php).
  • The only file that would need to be loaded is autoload_static.php (Phan already declared ClassLoader)

src/codebase.php Outdated
return new CodeBase(
$internal_class_name_list,
$internal_interface_name_list,
$internal_trait_name_list,
CodeBase::getPHPInternalConstantNameList(),
$internal_function_name_list
$internal_function_name_list,
$class_resolver
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this feature will not be enabled for all projects. There are PHP projects that don't even use composer (E.g. collections of standalone scripts).

src/Phan/CLI.php parses the command line options (E.g. options to set the analyzed project directory), so the ClassLoader instance to be used should be determined there, after the project directory is set.

@@ -753,14 +766,64 @@ private function resolveClassAliasesForAliasSet(FullyQualifiedClassName $origina
* True if a Clazz with the given FQSEN exists
*/
public function hasClassWithFQSEN(
FullyQualifiedClassName $fqsen
FullyQualifiedClassName $fqsen,
bool $autoload = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new feature, and should be disabled by default.

Still needs to be addressed. E.g. add a new option --use-project-composer-autoloader in src/Phan/CLI.php (see other comment in codebase.php).

Could you point me at a small example project and .phan/config.php and composer.json as an example of what you're referring to? (I'm assuming the exact command used would be phan path/to/file.php) I'm not clear on what you mean.

  • Is there a directory/file list for .phan/config.php in your use case?

* for Phan to skip analyzing the entire codebase and rather, use the ClassResolver to attempt to resolve classes
* at evaluation time, and dynamically analyze new classes as they are found.
*
* @var ClassResolver\ClassResolverInterface|null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention in the project is to use Phan\ClassResolver\ClassResolverInterface at the top, and use only the last part (class name) in the comments.

(There's an exception for ambiguities such as ast\Node and Microsoft\PhpParser\Node when both are used in the same file, but that is rare)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


$file_path = realpath($file);

if (!file_exists($file) || !$file_path) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be file_exists($file_path) (And check if file_path is empty first (i.e. on the left) -- Short circuiting || evaluates the left hand side first)

$file_path = realpath($file);

if (!file_exists($file) || !$file_path) {
throw new FileNotFoundException(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Emit an issue instead (E.g. with Issue::maybeEmit()). You can create a new Context()->withFile($file_path)

Something like PhanComposerLoaderMissingFile: Phan expected this file to exist for {CLASSLIKE} but no such file exists. (see src/Phan/Issue.php)


// Attempt to make file relative to cwd
$cwd = getcwd();
if (strpos($file_path, $cwd) === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use FileRef::getProjectRelativePath instead

@oligriffiths oligriffiths changed the title Dynamic class resolution at execution time, using Composer WIP Dynamic class resolution at execution time, using Composer Mar 21, 2018
@TysonAndre
Copy link
Member

Long-term, I'm in favor of having a mode to infer dependencies from the composer configuration. (e.g. it should be possible to run Phan if composer.json and vendor/ exist, but .phan doesn't.)
@oligriffiths - Do you mind if I add commits to this to make this an optional mode, e.g. (./phan --infer-dependencies-from-composer or something roughly along those lines, as well as a config option that allows user to avoid specifying directory_list.)

  • Existing unit tests will need to run and pass before this can be released.

@oligriffiths
Copy link
Author

Sounds good. I have a branch in progress and working on tests. Let me push what I have tomorrow but I’m pretty close to getting this working.

Copy link
Member

@TysonAndre TysonAndre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look like improvements, though the build is still failing.

Aside: Phan has a 'parse' phase, a 'methods' phase (e.g. to inherit types from the phpdoc of a parent method), some other phases, and an 'analysis' phase. There will probably still be edge cases when using this new mode (i.e. analysis results differing from manually putting the files in directory_list), but I'm fine with labelling this as experimental once I see it working on a few projects

  • Language server + autoloading might also have other edge cases, and could end up being slower

A note that can be left for followup PRs

An approach I saw elsewhere was to try to identify obvious class references while parsing (e.g. new ClassName(), ClassName::someMethod) and add them to the parse list during the parse phase (Breadth first search or depth first search). It might be worth looking into doing this in ParseVisitor (only when using autoloaders).

  • Maybe this could be limited to param, return, extended/used/implemented classes/traits/interfaces and property types would need to be added (during parse phase) for files that are excluded from analysis. (i.e. wouldn't have to analyze method invocations, etc. within those files)

src/Phan/CLI.php Outdated
@@ -103,7 +103,8 @@ class CLI
'target-php-version',
'unused-variable-detection',
'use-fallback-parser',
'version',
'use-project-composer-autoloader',
'version',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Fix spacing. internal/phpcbf can do that (any any other style fixes) if you have phpcbf.phar in your $PATH

src/Phan/CLI.php Outdated
);
if (Config::getValue('use_project_composer_autoloader')) {
// Set files as any remaining args on the CLI
$this->file_list_in_config = array_slice($argv, 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still want to be able to have configured files in .phan/config.php.

Examples of why files would be configured at the same time use_project_composer_autoloader is used:

  • Specify non-standard lib/ files, scripts, folders, etc. (e.g. Phan uses internal/ for some of it's own scripts)
  • Global constants and functions (e.g. in vendor/) can't be autoloaded. You need to be able to specify the path to some_custom_dumper() if a composer module provides that
  • Providing them in the CLI invocation every time is inconvenient.

Also, why is this setting file_list_only?


The way I see this being used is:

  • Users no longer have to specify vendor/A/B/src in directory_list and file_list, but specify their own source folders, miscellaneous scripts, etc to analyze
  • So they remove all of the vendor/A/B/src entries and enable use_project_composer_autoloader in .phan/config.php after upgrading to a Phan version that supports use_project_composer_autoloader
    • Users may still need to add some files in vendor/ manually because of class_alias(), autoloaders being unable to autoload global functions/constants, etc.

/**
* Map of previously resolved classes
*
* @var <string,string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to @var array<string,string>

*/
public function init()
{
$class_loader_class = Config::getValue('class_resolver');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support aliases for these ('composer', 'reflection') instead of making users provide the class name in config files.

/**
* Initialization hook called after the CLI args are parsed and Config has been initialized
*
* @throws FileNotFoundException If the configured
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finish the sentence fragment

@@ -29,6 +29,9 @@
// Create our CLI interface and load arguments
$cli = new CLI();

// Initialize the codebase, allows for pre-run setup
$code_base->init();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add this to plugins/codeclimate/engine (currently unused, but might as well be consistent about calling this before analyzeFileList)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

exit(EXIT_FAILURE);
} catch (\Throwable $throwable) {
// Catch miscellaneous errors such as $throwable and print their stack traces.
error_log("While parsing $file_path, caught: " . $throwable . "\n");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove debugging code - This will crash the language server, etc.

Prefer writing errors with fwrite(STDERR, ...)

Also, could emit a new type of Issue::Unanalyzable here to report the error


// Reset temp state
unset($this->class_resolver_parsing[$class]);
$this->setCurrentParsedFile(null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call getCurrentParsedFile at the start and setCurrentParsedFile in a finally block.


// Save this to the set of files to analyze
$analyze_file_path_list[] = $file_path;
} catch (\AssertionError $assertion_error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What emits AssertionError?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copied this block from here

Analysis::parseFile($code_base, $file_path);


try {
// Parse the file
Analysis::parseFile($this, $file_path);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a check that we don't parse a given file twice?

E.g. if the user-defined or composer autoloader falsely reports that A1 and A2 are in vendor/x/y/a.php (and it only contains A3), I think this might end up parsing a.php twice, which would lead to false positive PhanDuplicateClass, etc.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is now

@TysonAndre
Copy link
Member

You should add vendor/composer to .phan/config.php in the directory_list

Aside: tests can be run locally with tests/run_all_tests on Mac/linux

Running test suite: __FakeSelfFallbackTest
src/Phan/ClassResolver/ComposerResolver.php:16 PhanUndeclaredTypeProperty Property \Phan\ClassResolver\ComposerResolver::composer_class_loader has undeclared type \Composer\Autoload\ClassLoader
src/Phan/ClassResolver/ComposerResolver.php:21 PhanUnextractableAnnotation Saw unextractable annotation for comment '* @var <string,string>'
src/Phan/ClassResolver/ComposerResolver.php:31 PhanUndeclaredTypeParameter Parameter of undeclared type \Composer\Autoload\ClassLoader
src/Phan/ClassResolver/ComposerResolver.php:46 PhanUndeclaredClassMethod Call to method findFile from undeclared class \Composer\Autoload\ClassLoader
src/Phan/ClassResolver/ReflectionResolver.php:9 PhanUnreferencedClass Possibly zero references to class \Phan\ClassResolver\ReflectionResolver
src/Phan/ClassResolver/ReflectionResolver.php:33 PhanPossiblyFalseTypeReturn Returning type false|string but fileForClass() is declared to return string (false is incompatible)
src/Phan/CodeBase.php:247 PhanPluginUnknownMethodReturnType Method \Phan\CodeBase::init has no declared or inferred return type
src/Phan/CodeBase.php:253 PhanThrowTypeMismatch \Phan\CodeBase::init() throws \Phan\Exception\ClassNotFoundException, but it only has declarations of '@throws \Phan\Exception\FileNotFoundException'
src/Phan/CodeBase.php:281 PhanUnreferencedPublicMethod Possibly zero references to public method \Phan\CodeBase::getClassResolver
src/Phan/CodeBase.php:289 PhanPluginUnknownMethodReturnType Method \Phan\CodeBase::setClassResolver has no declared or inferred return type
src/Phan/CodeBase.php:943 PhanUndeclaredVariableDim Variable $analyze_file_path_list was undeclared, but array fields are being added to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants