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

5.22.0 "Could not get storage class for SomeInterface" #10708

Closed
florisluiten opened this issue Feb 15, 2024 · 7 comments
Closed

5.22.0 "Could not get storage class for SomeInterface" #10708

florisluiten opened this issue Feb 15, 2024 · 7 comments

Comments

@florisluiten
Copy link
Contributor

I'm having a related issue to #10706 , preventing me from updating to version 5.22.0. I've used the Changelog and discovered that when I revert the changes from https://github.com/vimeo/psalm/pull/10603/files#diff-90d3f6742878717c6bf051442dc8b3782579ea1b21bde8195cd47b3af03f456e the issue is gone.

My biggest problem is that Psalm crashes, giving me no clue about what file actually triggered the issue. This also makes it very hard to pin down the problem. Also, I use a baseline file that probably has the issue ignored, but the error occurs during the "parsing" phase, so I think that the baseline has no impact.

I've noticed that running it with the --debug flag makes the issue disappear. I also noticed that the problem is only triggered in threaded mode (--threads=4). This also makes it very hard for me to debug the issue since a step-debugger doesn't seem to work in threaded mode; and when ran with --threads=1 the step-debugger works but the issue is not triggered. This makes a simple STR very hard.

I think that there are two problems:

  1. Obviously, the issue that triggers the "Could not get class storage for X" as addressed in 5.22.0 regression: Could not get class storage for psl\range\upperboundrangeinterface #10706
  2. The fact that Psalm crashes, giving no clue about how to work around the issue

The second problem can be reproduced with https://psalm.dev/r/fe6bc2aace ; As seen, Psalm crashes and gives no useful feedback. However, with https://psalm.dev/r/9aa415b204 the crash does not occur and the user can actually see the problem, which is way more helpful.

TLDR: Psalm crashes with "Could not get storage class for SomeInterface", and on a large codebase with a baseline file I find it impossible to find the cause of the problem and this prevents me from updating to 5.22.0.

Copy link

I found these snippets:

https://psalm.dev/r/fe6bc2aace
<?php

class Foo {
    /**
     * @global static $bar
     * @return void
     */
    public function instance() {
        global $bar;

        $bar->instance();
    }
}
Psalm encountered an internal error:

/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php: Could not get class storage for static
https://psalm.dev/r/9aa415b204
<?php

class Foo {
    /**
     * @global Wrong $bar
     * @return void
     */
    public function instance() {
        global $bar;

        $bar->instance();
    }
}
Psalm output (using commit 639bed0):

ERROR: UndefinedClass - 11:9 - Class, interface or enum named Wrong does not exist

@weirdan
Copy link
Collaborator

weirdan commented Feb 15, 2024

when I revert the changes from #10603 (files) the issue is gone.

This probably means Psalm didn't really work correctly on your codebase before. The problem could manifest itself as a wrong type inference, appearing and disappearing depending on the number and order of files Psalm scanned during the check, for example. Now it's an outright crash, and this means we're one step closer to the actual fix.

This also makes it very hard for me to debug the issue since a step-debugger doesn't seem to work in threaded mode;

I think Xdebug itself can work with multiple forked processes, according to the discussion in https://bugs.xdebug.org/view.php?id=1771. They also mention that some IDEs may limit the number of concurrent debug connections to, like, 1.

@florisluiten
Copy link
Contributor Author

florisluiten commented Feb 16, 2024

Thanks @weirdan , I think you are right about the existing problem being the root cause.

Debugging with Xdebug and PhpStorm does not work, PhpStorm keeps complaining about the "SERVER_NAME" not being defined in $_SERVER once in threaded mode (it works fine before forking, even though there is no "SERVER_NAME" configured then either...).

So I've switched to dbgp which works fine, even with threading. Tip for anyone trying this: Once the process forks, it tries to setup a new debug connection in the background, but the first debugging process blocks the incoming request. So first do a run, wait some seconds until the process has been forked and than use CTRL+C to detach the first session and "step into" the second session where the first forked process runs. Then set a breakpoint and do run.

So, I'm now able to step into the situation where Psalm crashes. The stacktrace reads:

myproject/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php:726: Psalm\Internal\Codebase\ClassLikes->getParentInterfaces
myproject/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php:716: Psalm\Internal\Codebase\ClassLikes->interfaceExtends
myproject/vendor/vimeo/psalm/src/Psalm/Codebase.php:763: Psalm\Codebase->interfaceExtends
myproject/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php:357: Psalm\Internal\Type\Comparator\ObjectComparator::isIntersectionShallowlyContainedBy
myproject/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php:117: Psalm\Internal\Type\Comparator\ObjectComparator::isShallowlyContainedBy
myproject/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php:350: Psalm\Internal\Type\Comparator\AtomicTypeComparator::isContainedBy
myproject/vendor/vimeo/psalm/src/Psalm/Type.php:891: Psalm\Type::intersectAtomicTypes
myproject/vendor/vimeo/psalm/src/Psalm/Type.php:768: Psalm\Type::intersectUnionTypes
myproject/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php:112: Psalm\Internal\PhpVisitor\Reflector\TypeHintResolver::resolve
myproject/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php:815: Psalm\Internal\PhpVisitor\Reflector\FunctionLikeNodeScanner->getTranslatedFunctionParam
myproject//vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php:189: Psalm\Internal\PhpVisitor\Reflector\FunctionLikeNodeScanner->start
myproject//vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php:207: Psalm\Internal\PhpVisitor\ReflectorVisitor->enterNode
myproject//vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:200: PhpParser\NodeTraverser->traverseArray
myproject//vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114: PhpParser\NodeTraverser->traverseNode
myproject//vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223: PhpParser\NodeTraverser->traverseArray
myproject//vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:114: PhpParser\NodeTraverser->traverseNode
myproject//vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:223: PhpParser\NodeTraverser->traverseArray
myproject//vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:91: PhpParser\NodeTraverser->traverse
myproject//vendor/vimeo/psalm/src/Psalm/Internal/Scanner/FileScanner.php:79: Psalm\Internal\Scanner\FileScanner->scan
myproject//vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php:554: Psalm\Internal\Codebase\Scanner->scanFile
myproject//vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php:782: Psalm\Internal\Codebase\Scanner->scanAPath
myproject//vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php:191: Psalm\Internal\Fork\Pool->__construct
myproject//vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php:332: Psalm\Internal\Codebase\Scanner->scanFilePaths
myproject//vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php:280: Psalm\Internal\Codebase\Scanner->scanFiles
myproject//vendor/vimeo/psalm/src/Psalm/Codebase.php:505: Psalm\Codebase->scanFiles
myproject//vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php:510: Psalm\Internal\Analyzer\ProjectAnalyzer->check
myproject//vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php:374: Psalm\Internal\Cli\Psalm::run
myproject//vendor/vimeo/psalm/psalm:9: include
myproject//vendor/bin/psalm:130: {main}

I can also see that the class requested is not present in the $storage array, so it makes sense that it throws there. However, I do not understand why it is not in that array. There are 33 classes in $storage, way less than I have in my codebase (but a lot of legacy files that probably contain numerous "errors"). The requested class exists, but probably is not processed but already referenced? I do not understand enough of the Psalm internals to fully understand this.

Hope this helps you pointing the exact cause. If I need to do some more debugging I'm willing to help, just give me some pointers :)

Thanks for your help so far.

@tm1000
Copy link
Contributor

tm1000 commented Feb 16, 2024

@florisluiten See line 4 of your output

myproject/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php:357: Psalm\Internal\Type\Comparator\ObjectComparator::isIntersectionShallowlyContainedBy

Then this:

#10706 (comment)

@weirdan
Copy link
Collaborator

weirdan commented Feb 19, 2024

Please try with composer require --dev vimeo/psalm:dev-10706-catch-intersection-exceptions-during-scanning

@florisluiten
Copy link
Contributor Author

@weirdan I can confirm that Psalm no longer crashes! 🎊

Also, the output doesn't throw any (new) warnings, so all is well. Thanks for your help!

@weirdan
Copy link
Collaborator

weirdan commented Feb 23, 2024

Fixed in 5.22.2

@weirdan weirdan closed this as completed Feb 23, 2024
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

No branches or pull requests

3 participants