vimeo / psalm

A static analysis tool for finding errors in PHP applications
https://psalm.dev
MIT License
5.57k stars 660 forks source link

Uncaught Exception: InvalidArgumentException Could not get class storage for class-string-map #8626

Closed yunosh closed 1 year ago

yunosh commented 2 years ago

Emitted in /Users/janschneider/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php:45

This is a strange class name it is looking for, obviously not from my code base. Running psalm with --debug-by-line reveals that it stops at line 111 of a compiled Symfony container:

110:        $this->privates['service_container'] = function () {
111:            include_once \dirname(__DIR__, 4).'/vendor/league/openapi-psr7-validator/src/PSR7/ValidatorBuilder.php';
112:            include_once \dirname(__DIR__, 4).'/src/EventListener/RequestLanguageListener.php';
psalm-github-bot[bot] commented 2 years ago

Hey @yunosh, can you reproduce the issue on https://psalm.dev ?

orklah commented 2 years ago

class-string-map is a Psalm's pseudo type that describe an array which has class-string offsets and for each offset, contains an instance of that class-string.

I'm not sure why Psalm would fail to recognize it though. Do you have the latest Psalm version? Do you know what /src/EventListener/RequestLanguageListener.php contains?

yunosh commented 2 years ago

Yes, this is both with Psalm 4.15.0 (locally) as with the latest 4.29.0 (inside Docker container). RequestLanguageListener ist harmless:

<?php
declare(strict_types = 1);

namespace XXX\EventListener;

use Symfony\Component\HttpKernel\Event\RequestEvent;

final class RequestLanguageListener
{

    public function onKernelRequest(RequestEvent $event): void
    {
        if (!$event->isMainRequest()) {
            return;
        }

        $request = $event->getRequest();
        $request->setLocale($request->headers->get('Accept-Language', 'de-DE'));
    }

}
weirdan commented 2 years ago

class-string-map is likely something used (or inferred) in the container, as it will likely contain a map of class/interface names to instances of those classes/interfaces. Can you upload your compiled container somewhere for us to see?

yunosh commented 1 year ago

If you are talking about the Symfony container of the app that's being analyzed, this won't help, because the error even occurs if there is no container at all. Or do you mean the container of Psalm (does it have one?)? That's running off a separate Docker instance.

weirdan commented 1 year ago

I mean the DI container used by Symfony.

Also, it would be useful to see the backtrace for this exception.

yunosh commented 1 year ago

Yes, I figured that you might talk about the Symfony container, but I still don't know which Symfony container, please see my last question. Or maybe I need to tell you more about the setup. Psalm is executed, along other checks, within a Docker container, running CaptainHook as a pre-commit hook. This is the backtrace:

Uncaught InvalidArgumentException: Could not get class storage for class-string-map in /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php:46
Stack trace:
#0 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeChecker.php(364): Psalm\Internal\Provider\ClassLikeStorageProvider->get('class-string-ma...')
#1 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeChecker.php(125): Psalm\Internal\TypeVisitor\TypeChecker->checkTemplateParam(Object(Psalm\Type\Atomic\TTemplateParam))
#2 /pre-commit/vendor/vimeo/psalm/src/Psalm/Type/NodeVisitor.php(20): Psalm\Internal\TypeVisitor\TypeChecker->enterNode(Object(Psalm\Type\Atomic\TTemplateParam))
#3 /pre-commit/vendor/vimeo/psalm/src/Psalm/Type/NodeVisitor.php(31): Psalm\Type\NodeVisitor->traverse(Object(Psalm\Type\Atomic\TTemplateParam))
#4 /pre-commit/vendor/vimeo/psalm/src/Psalm/Type/NodeVisitor.php(31): Psalm\Type\NodeVisitor->traverse(Object(Psalm\Type\Union))
#5 /pre-commit/vendor/vimeo/psalm/src/Psalm/Type/NodeVisitor.php(31): Psalm\Type\NodeVisitor->traverse(Object(Psalm\Type\Atomic\TGenericObject))
#6 /pre-commit/vendor/vimeo/psalm/src/Psalm/Type/NodeVisitor.php(31): Psalm\Type\NodeVisitor->traverse(Object(Psalm\Type\Union))
#7 /pre-commit/vendor/vimeo/psalm/src/Psalm/Type/NodeVisitor.php(45): Psalm\Type\NodeVisitor->traverse(Object(Psalm\Type\Atomic\TClassStringMap))
#8 /pre-commit/vendor/vimeo/psalm/src/Psalm/Type/Union.php(1449): Psalm\Type\NodeVisitor->traverseArray(Array)
#9 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php(844): Psalm\Type\Union->check(Object(Psalm\Internal\Analyzer\MethodAnalyzer), Object(Psalm\CodeLocation\DocblockTypeLocation), Array, Array, false, false)
#10 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(417): Psalm\Internal\Analyzer\FunctionLike\ReturnTypeAnalyzer::checkReturnType(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Internal\Analyzer\ProjectAnalyzer), Object(Psalm\Internal\Analyzer\MethodAnalyzer), Object(Psalm\Storage\MethodStorage), Object(Psalm\Context))
#11 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1798): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#12 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(425): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context))
#13 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(229): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#14 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(362): Psalm\Internal\Analyzer\FileAnalyzer->analyze()
#15 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(619): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(2892, '/app/design-sui...')
#16 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(291): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#17 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(1210): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, false)
#18 /pre-commit/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php(375): Psalm\Internal\Analyzer\ProjectAnalyzer->checkPaths(Array)
#19 /pre-commit/vendor/vimeo/psalm/psalm(7): Psalm\Internal\Cli\Psalm::run(Array)
#20 /pre-commit/vendor/bin/psalm(120): include('/pre-commit/ven...')
#21 {main}
(Psalm 4.29.0@7ec5ffbd5f68ae03782d7fd33fff0c45a69f95b3 crashed due to an uncaught Throwable)
weirdan commented 1 year ago

this won't help, because the error even occurs if there is no container at all.

Can you clarify what you mean by 'no container at all'?

yunosh commented 1 year ago

No Symfony DI container inside the Symfony application, i.e. if you run Psalm on the Symfony application's directory without it having warmed up the cache, and thus without having generated the container.

ptomulik commented 1 year ago

I've seen something similar in #9401