Skip to content

Cannot create UnionType when match arm has multiple expressions and throws an exception #12512

@22h

Description

@22h

Bug report

I have a problem with the combination of enum and match. As soon as a match arm has multiple expressions and throws an exception as a result, phpstan breaks.

I have tried several combinations. All with phpstan in version 2.1, 1.12.16, 1.12.15

This was my local stacktrace

Internal error:  Cannot create PHPStan\Type\UnionType with: 'FooBarEnum', 'FooBarEnum' while analysing file

## phar:///vendor/phpstan/phpstan/phpstan.phar/src/Type/UnionType.php(69)                                                                                              
#0 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Type/UnionType.php(83): PHPStan\Type\UnionType::PHPStan\Type\{closure}()                                            
#1 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php(31): PHPStan\Type\UnionType->__construct()                     
#2 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1774):                                                                                   
PHPStan\Type\Php\GetDebugTypeFunctionReturnTypeExtension->getTypeFromFunctionCall()                                                                                                                      
#3 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(622): PHPStan\Analyser\MutatingScope->resolveType()                                      
#4 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Type/Php/SprintfFunctionDynamicReturnTypeExtension.php(237): PHPStan\Analyser\MutatingScope->getType()              
#5 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Type/Php/SprintfFunctionDynamicReturnTypeExtension.php(51):                                                         
PHPStan\Type\Php\SprintfFunctionDynamicReturnTypeExtension->getConstantType()                                                                                                                            
#6 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1774):                                                                                   
PHPStan\Type\Php\SprintfFunctionDynamicReturnTypeExtension->getTypeFromFunctionCall()                                                                                                                    
#7 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(622): PHPStan\Analyser\MutatingScope->resolveType()                                      
#8 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(3656): PHPStan\Analyser\MutatingScope->getType()                                     
#9 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(2629): PHPStan\Analyser\NodeScopeResolver->processArgs()                             
#10 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(2889): PHPStan\Analyser\NodeScopeResolver->processExprNode()                        
#11 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(2825): PHPStan\Analyser\NodeScopeResolver->processExprNode()                        
#12 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(700): PHPStan\Analyser\NodeScopeResolver->processExprNode()                         
#13 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(465): PHPStan\Analyser\NodeScopeResolver->processStmtNode()                         
#14 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(653): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()                        
#15 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(465): PHPStan\Analyser\NodeScopeResolver->processStmtNode()                         
#16 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(793): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()                        
#17 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(465): PHPStan\Analyser\NodeScopeResolver->processStmtNode()                         
#18 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(760): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()                        
#19 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(430): PHPStan\Analyser\NodeScopeResolver->processStmtNode()                         
#20 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(179): PHPStan\Analyser\NodeScopeResolver->processNodes()                                 
#21 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(139): PHPStan\Analyser\FileAnalyser->analyseFile()                                       
#22 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/EventEmitterTrait.php(111): PHPStan\Command\WorkerCommand::PHPStan\Command\{closure}()  
#23 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-react/src/Decoder.php(117): _PHPStan_2132cc0bd\Evenement\EventEmitter->emit()                       
#24 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/EventEmitterTrait.php(111): _PHPStan_2132cc0bd\Clue\React\NDJson\Decoder->handleData()  
#25 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/Util.php(62): _PHPStan_2132cc0bd\Evenement\EventEmitter->emit()                                
#26 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/EventEmitterTrait.php(111):                                                             
_PHPStan_2132cc0bd\React\Stream\Util::_PHPStan_2132cc0bd\React\Stream\{closure}()                                                                                                                        
#27 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/DuplexResourceStream.php(168): _PHPStan_2132cc0bd\Evenement\EventEmitter->emit()               
#28 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(201):                                                                 
_PHPStan_2132cc0bd\React\Stream\DuplexResourceStream->handleData()                                                                                                                                       
#29 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(173):                                                                 
_PHPStan_2132cc0bd\React\EventLoop\StreamSelectLoop->waitForStreamActivity()                                                                                                                             
#30 phar:///vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(99): _PHPStan_2132cc0bd\React\EventLoop\StreamSelectLoop->run()                          
#31 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259): PHPStan\Command\WorkerCommand->execute()                              
#32 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870): _PHPStan_2132cc0bd\Symfony\Component\Console\Command\Command->run()       
#33 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261): _PHPStan_2132cc0bd\Symfony\Component\Console\Application->doRunCommand()  
#34 phar:///vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157): _PHPStan_2132cc0bd\Symfony\Component\Console\Application->doRun()         
#35 phar:///vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(127): _PHPStan_2132cc0bd\Symfony\Component\Console\Application->run()                                      
#36 phar:///vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(128): _PHPStan_2132cc0bd\{closure}()                                                                       
#37 /vendor/phpstan/phpstan/phpstan(8): require('...')                                                                                                                 
#38 /vendor/bin/phpstan(120): include('...')                                                                                                                           
#39 {main} 

Error: https://phpstan.org/r/9bb57ef8-e24d-498c-854a-7c0385faf32d

<?php declare(strict_types = 1);

enum FooBarEnum: string
{
    case CASE_ONE = 'case_one';
    case CASE_TWO = 'case_two';
    case CASE_THREE = 'case_three';
    case CASE_FOUR = 'case_four';

    public function matchFunction(): string
    {
        return match ($this) {
            self::CASE_ONE => 'one',
            self::CASE_TWO => 'two',
	    default => throw new \Exception(
                sprintf('"%s" is not implemented yet', get_debug_type($this))
            )
        };
    }
}

Same error: https://phpstan.org/r/7620c40a-efce-41ad-bedd-e27af0d1b5c7

<?php declare(strict_types = 1);

enum FooBarEnum: string
{
    case CASE_ONE = 'case_one';
    case CASE_TWO = 'case_two';
    case CASE_THREE = 'case_three';
    case CASE_FOUR = 'case_four';

    public function matchFunction(): string
    {
        return match ($this) {
            self::CASE_ONE => 'one',
            self::CASE_TWO => 'two',
	    self::CASE_THREE, self::CASE_FOUR => throw new \Exception(
                sprintf('"%s" is not implemented yet', get_debug_type($this))
            )
        };
    }
}

No error: https://phpstan.org/r/569233fd-482f-4b24-a5cc-f9641111d422

<?php declare(strict_types = 1);

enum FooBarEnum: string
{
    case CASE_ONE = 'case_one';
    case CASE_TWO = 'case_two';
    case CASE_THREE = 'case_three';
    case CASE_FOUR = 'case_four';

    public function matchFunction(): string
    {
        return match ($this) {
            self::CASE_ONE => 'one',
            self::CASE_TWO => 'two',
	    self::CASE_THREE => 'three',
	    self::CASE_FOUR => throw new \Exception(
                sprintf('"%s" is not implemented yet', get_debug_type($this))
            )
        };
    }
}

Also no error: https://phpstan.org/r/7d6e61b8-3429-469f-bab3-26c1dd506b8b

<?php declare(strict_types = 1);

enum FooBarEnum: string
{
    case CASE_ONE = 'case_one';
    case CASE_TWO = 'case_two';
    case CASE_THREE = 'case_three';
    case CASE_FOUR = 'case_four';

    public function matchFunction(): string
    {
        return match ($this) {
            self::CASE_ONE => 'one',
            self::CASE_TWO => 'two',
	    self::CASE_THREE => 'three',
	    default => throw new \Exception(
                sprintf('"%s" is not implemented yet', get_debug_type($this))
            )
        };
    }
}

This works also: https://phpstan.org/r/08663d62-9ae3-4f97-85f0-2dc7ddf23a8f

<?php declare(strict_types = 1);

enum FooBarEnum: string
{
    case CASE_ONE = 'case_one';
    case CASE_TWO = 'case_two';
    case CASE_THREE = 'case_three';
    case CASE_FOUR = 'case_four';

    public function matchFunction(): string
    {
        return match ($this) {
            self::CASE_ONE => 'one',
            self::CASE_TWO => 'two',
	    self::CASE_THREE => throw new \Exception(
                sprintf('"%s" is not implemented yet', get_debug_type($this))
            ),
	    default => throw new \Exception(
                sprintf('"%s" is not implemented yet', get_debug_type($this))
            )
        };
    }
}

Code snippet that reproduces the problem

https://phpstan.org/r/9bb57ef8-e24d-498c-854a-7c0385faf32d

Expected output

Nothing

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

PHPStan has often saved me from minor or major bugs. PHPStan is one of those tools that you get to know and never want to miss in a project again.

I was also very happy about the presentation at the last Symfony conference.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions