phpstan-doctrine icon indicating copy to clipboard operation
phpstan-doctrine copied to clipboard

PHPStan crashes if Doctrine Column is a UnitEnum (PHPStan+Doctrine extension)

Open Firehed opened this issue 1 year ago • 3 comments
trafficstars

Bug report

This indicates an error in my code, but the crash is unexpected. I'm providing the bug report and stack trace as the PHPStan output requests, but through some quick trial-and-error I was able to determine that #[Mapping\Column] on a class property that's unsupported would trigger it.

Not sure if this would happen if Doctrine had a custom type mapper set which allows this to work (seeing the stack trace, it looks like probably), but I think PHPStan, or more specifically the Doctrine extension, should catch the assertion error and translate it into an analysis warning of some kind.

Simple reproduce code:

<?php

declare(strict_types=1);

namespace App\Entities;

use Doctrine\ORM\Mapping;

#[Mapping\Entity]
class Bug
{
    #[Mapping\Column]
    #[Mapping\Id]
    public string $id;

    #[Mapping\Column]
    public Status $status;

    public function causeError(): void
    {
        $this->a = 1; // This should error, instead PHPStan crashes
    }
}

enum Status
{
    case Working;
    case Broken;
}
# $ cat phpstan.neon
includes:
    # phpstan-baseline.neon
    - vendor/phpstan/phpstan-phpunit/extension.neon
    - vendor/phpstan/phpstan-strict-rules/rules.neon
    - vendor/phpstan/phpstan-deprecation-rules/rules.neon
    - vendor/phpstan/phpstan-doctrine/extension.neon
    - vendor/phpstan/phpstan-doctrine/rules.neon
parameters:
    excludePaths:
        - rector.php
        - vendor
    level: max
    paths:
        - .
    universalObjectCratesClasses:
        - Stripe\StripeObject
Internal error: Internal error: assert($type instanceof ReflectionNamedType) while analysing file (...)

     ## (path)/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultTypedFieldMapper.php(62)
     #0 (path)/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultTypedFieldMapper.php(62): assert(false, 'assert($type in...')
     phpstan/phpstan#1 (path)/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php(1594): Doctrine\ORM\Mapping\DefaultTypedFieldMapper->validateAndComplete(Array, Object(ReflectionProperty))
     phpstan/phpstan#2 (path)/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php(1644): Doctrine\ORM\Mapping\ClassMetadataInfo->validateAndCompleteTypedFieldMapping(Array)
     phpstan/phpstan#3 (path)/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php(2723): Doctrine\ORM\Mapping\ClassMetadataInfo->validateAndCompleteFieldMapping(Array)
     phpstan/phpstan#4 (path)/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php(369): Doctrine\ORM\Mapping\ClassMetadataInfo->mapField(Array)
     phpstan/phpstan#5 (path)/vendor/phpstan/phpstan-doctrine/src/Doctrine/Mapping/MappingDriverChain.php(31): Doctrine\ORM\Mapping\Driver\AttributeDriver->loadMetadataForClass('App\\Entities\\In...', Object(Doctrine\ORM\Mapping\ClassMetadata))
     phpstan/phpstan#6 (path)/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php(149): PHPStan\Doctrine\Mapping\MappingDriverChain->loadMetadataForClass('App\\Entities\\In...', Object(Doctrine\ORM\Mapping\ClassMetadata))
     phpstan/phpstan#7 (path)/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(343): Doctrine\ORM\Mapping\ClassMetadataFactory->doLoadMetadata(Object(Doctrine\ORM\Mapping\ClassMetadata), NULL, false, Array)
     phpstan/phpstan#8 (path)/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php(225): Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->loadMetadata('App\\Entities\\In...')
     phpstan/phpstan#9 (path)/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/ObjectMetadataResolver.php(129): Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor('App\\Entities\\In...')
     phpstan/phpstan#10 (path)/vendor/phpstan/phpstan-doctrine/src/Rules/Doctrine/ORM/PropertiesExtension.php(27): PHPStan\Type\Doctrine\ObjectMetadataResolver->getClassMetadata('App\\Entities\\In...')
     phpstan/phpstan#11 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/src/Rules/DeadCode/UnusedPrivatePropertyRule.php(100): PHPStan\Rules\Doctrine\ORM\PropertiesExtension->isAlwaysRead(Object(PHPStan\Reflection\Php\PhpPropertyReflection), 'amount')
     phpstan/phpstan#12 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(107): PHPStan\Rules\DeadCode\UnusedPrivatePropertyRule->processNode(Object(PHPStan\Node\ClassPropertiesNode), Object(PHPStan\Analyser\MutatingScope))
     phpstan/phpstan#13 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(669): PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}(Object(PHPStan\Node\ClassPropertiesNode), Object(PHPStan\Analyser\MutatingScope))
     phpstan/phpstan#14 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(401): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Class_), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
     phpstan/phpstan#15 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(640): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Namespace_), Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure),
     Object(PHPStan\Analyser\StatementContext))
     phpstan/phpstan#16 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(370): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Namespace_), Object(PHPStan\Analyser\MutatingScope), Object(Closure),
     Object(PHPStan\Analyser\StatementContext))
     phpstan/phpstan#17 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(166): PHPStan\Analyser\NodeScopeResolver->processNodes(Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure))
     phpstan/phpstan#18 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(132): PHPStan\Analyser\FileAnalyser->analyseFile('/Users/firehed/...', Array, Object(PHPStan\Rules\LazyRegistry), Object(PHPStan\Collectors\Registry), NULL)
     phpstan/phpstan#19 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): PHPStan\Command\WorkerCommand->PHPStan\Command\{closure}(Array)
     phpstan/phpstan#20 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-react/src/Decoder.php(117): _PHPStan_3d4486d07\Evenement\EventEmitter->emit('data', Array)
     phpstan/phpstan#21 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): _PHPStan_3d4486d07\Clue\React\NDJson\Decoder->handleData(Array)
     phpstan/phpstan#22 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/Util.php(62): _PHPStan_3d4486d07\Evenement\EventEmitter->emit('data', Array)
     phpstan/phpstan#23 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): _PHPStan_3d4486d07\React\Stream\Util::_PHPStan_3d4486d07\React\Stream\{closure}('{"action":"anal...')
     phpstan/phpstan#24 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/DuplexResourceStream.php(154): _PHPStan_3d4486d07\Evenement\EventEmitter->emit('data', Array)
     phpstan/phpstan#25 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(201): _PHPStan_3d4486d07\React\Stream\DuplexResourceStream->handleData(Resource id phpstan/phpstan#6555)
     phpstan/phpstan#26 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(173): _PHPStan_3d4486d07\React\EventLoop\StreamSelectLoop->waitForStreamActivity(NULL)
     phpstan/phpstan#27 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(98): _PHPStan_3d4486d07\React\EventLoop\StreamSelectLoop->run()
     phpstan/phpstan#28 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259): PHPStan\Command\WorkerCommand->execute(Object(_PHPStan_3d4486d07\Symfony\Component\Console\Input\ArgvInput),
     Object(_PHPStan_3d4486d07\Symfony\Component\Console\Output\ConsoleOutput))
     phpstan/phpstan#29 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870): _PHPStan_3d4486d07\Symfony\Component\Console\Command\Command->run(Object(_PHPStan_3d4486d07\Symfony\Component\Console\Input\ArgvInput),
     Object(_PHPStan_3d4486d07\Symfony\Component\Console\Output\ConsoleOutput))
     phpstan/phpstan#30 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261): _PHPStan_3d4486d07\Symfony\Component\Console\Application->doRunCommand(Object(PHPStan\Command\WorkerCommand), Object(_PHPStan_3d4486d07\Symfony\Component\Console\Input\ArgvInput),
     Object(_PHPStan_3d4486d07\Symfony\Component\Console\Output\ConsoleOutput))
     phpstan/phpstan#31 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157): _PHPStan_3d4486d07\Symfony\Component\Console\Application->doRun(Object(_PHPStan_3d4486d07\Symfony\Component\Console\Input\ArgvInput),
     Object(_PHPStan_3d4486d07\Symfony\Component\Console\Output\ConsoleOutput))
     phpstan/phpstan#32 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124): _PHPStan_3d4486d07\Symfony\Component\Console\Application->run()
     phpstan/phpstan#33 phar://(path)/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125): _PHPStan_3d4486d07\{closure}()
     phpstan/phpstan#34 (path)/vendor/phpstan/phpstan/phpstan(8): require('phar:///Users/f...')
     phpstan/phpstan#35 (path)/vendor/bin/phpstan(119): include('/Users/firehed/...')
     phpstan/phpstan#36 {main}

Code snippet that reproduces the problem

No response

Expected output

No crash, possibly an indication of an invalid column mapping.

Did PHPStan help you today? Did it make you happy in any way?

No response

Firehed avatar Jan 23 '24 19:01 Firehed

This bug report is missing a link to reproduction at phpstan.org/try.

It will most likely be closed after manual review.

mergeable[bot] avatar Jan 23 '24 19:01 mergeable[bot]

The crash is actually not in phpstan but in doctrine/orm based on the stack trace

stof avatar Apr 22 '24 15:04 stof