phpstan / phpstan-doctrine

Doctrine extensions for PHPStan
MIT License
595 stars 97 forks source link

Uncaught `AnnotationException` when entity uses `@note` annotation #420

Closed mbabker closed 1 year ago

mbabker commented 1 year ago

In a project no longer using annotations (still installed because dependency chains are long), we have a few @note annotations in classes for comments. When running PHPStan, we're getting an uncaught exception like this one:

Uncaught Doctrine\Common\Annotations\AnnotationException: [Semantical Error] The annotation "@note" in property App\Entity\Lot::$bid_end_time was never imported. Did you maybe forget to add a "use" statement for this annotation? in /app/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php:40

Minimal entity class

<?php declare(strict_types=1);

namespace App\Entity;

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table]
class Lot
{
    /**
     * @phpstan-var positive-int|null
     */
    #[ORM\Column]
    #[ORM\Id]
    #[ORM\GeneratedValue]
    private ?int $id = null;

    /**
     * For online only events, the time the event will end. This is generated by PHP's time() function for consistency.
     *
     * @note Keep private to force use of getter due to Doctrine internally casting bigint to string
     */
    #[ORM\Column(type: Types::BIGINT, nullable: true)]
    private ?string $bid_end_time = null;

    public function setBidEndTime(?int $bidEndTime): self
    {
        $this->bid_end_time = $bidEndTime !== null ? (string) $bidEndTime : null;

        return $this;
    }

    public function getBidEndTime(): ?int
    {
        // Doctrine maps bigint columns to strings for platform compatibility issues, we're safe casting to int for all cases
        return $this->bid_end_time ? (int) $this->bid_end_time : null;
    }
}

We do have a compiler pass in our Symfony app running $container->getDefinition('annotations.reader')->addMethodCall('addGlobalIgnoredName', ['note']); to ignore this on the annotation reader in the app itself, but it seems the reader PHPStan is using either needs a way to configure this or would force us to restore Doctrine\Common\Annotations\Annotation\IgnoreAnnotation annotations.

ondrejmirtes commented 1 year ago

Hello, you can work around this problem by providing objectManagerLoader: https://github.com/phpstan/phpstan-doctrine#configuration

That way, your compiler pass will run too.

mbabker commented 1 year ago

Just for completeness...

We've managed to phase the annotations integration out of (most of) our Symfony app. With the framework.annotations.enabled key set to false, the annotations.reader service won't exist, and there's (rightfully) nothing for that compiler pass to add a method call to.

I did manage to get a stack trace when I tried running things today:

Internal error: Internal error: [Semantical Error] The annotation "@note" in property App\Jobs\Entity\CronJob::$lastRunAt was never imported. Did you maybe forget to add a "use" statement for this annotation? in file    
/app/src/Jobs/Entity/CronJob.php                                                                                                                                                           

Post the following stack trace to https://github.com/phpstan/phpstan/issues/new?template=Bug_report.md:                                                                                                                       
#0 /app/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php(796): Doctrine\Common\Annotations\AnnotationException::semanticalError('The annotation ...')             
#1 /app/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php(712): Doctrine\Common\Annotations\DocParser->Annotation()                                                
#2 /app/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php(368): Doctrine\Common\Annotations\DocParser->Annotations()                                               
#3 /app/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php(174): Doctrine\Common\Annotations\DocParser->parse('/**\n * @note Ca...', 'property App\\...')  
#4 /app/vendor/phpstan/phpstan-doctrine/src/Rules/Gedmo/PropertiesExtension.php(86):                                                                                                       
Doctrine\Common\Annotations\AnnotationReader->getPropertyAnnotations(Object(PHPStan\BetterReflection\Reflection\Adapter\ReflectionProperty))                                                                                  
#5 /app/vendor/phpstan/phpstan-doctrine/src/Rules/Gedmo/PropertiesExtension.php(62):                                                                                                       
PHPStan\Rules\Gedmo\PropertiesExtension->isGedmoAnnotationOrAttribute(Object(PHPStan\Reflection\Php\PhpPropertyReflection), 'lastRunAt', Array)                                                                               
#6 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Rules/DeadCode/UnusedPrivatePropertyRule.php(108):                                                                                  
PHPStan\Rules\Gedmo\PropertiesExtension->isAlwaysWritten(Object(PHPStan\Reflection\Php\PhpPropertyReflection), 'lastRunAt')                                                                                                   
#7 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(106):                                                                                                     
PHPStan\Rules\DeadCode\UnusedPrivatePropertyRule->processNode(Object(PHPStan\Node\ClassPropertiesNode), Object(PHPStan\Analyser\MutatingScope))                                                                               
#8 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(600):                                                                                                
PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}(Object(PHPStan\Node\ClassPropertiesNode), Object(PHPStan\Analyser\MutatingScope))                                                                                   
#9 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(360): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Class_),        
Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))                                                                                                                           
#10 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(571): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Namespace_),  
Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))                                                                                                                    
#11 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(327): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Namespace_),   
Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))                                                                                                                           
#12 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(175): PHPStan\Analyser\NodeScopeResolver->processNodes(Array, Object(PHPStan\Analyser\MutatingScope),    
Object(Closure))                                                                                                                                                                                                              
#13 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(148): PHPStan\Analyser\FileAnalyser->analyseFile('/app...', Array,                            
Object(PHPStan\Rules\LazyRegistry), Object(PHPStan\Collectors\Registry), NULL)                                                                                                                                                
#14 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): PHPStan\Command\WorkerCommand->PHPStan\Command\{closure}(Array)    
#15 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-react/src/Decoder.php(110): _PHPStan_d3e3292d7\Evenement\EventEmitter->emit('data', Array)                          
#16 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): _PHPStan_d3e3292d7\Clue\React\NDJson\Decoder->handleData(Array)    
#17 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/Util.php(62): _PHPStan_d3e3292d7\Evenement\EventEmitter->emit('data', Array)                                   
#18 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):                                                                    
_PHPStan_d3e3292d7\React\Stream\Util::_PHPStan_d3e3292d7\React\Stream\{closure}('{"action":"anal...')                                                                                                                         
#19 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/DuplexResourceStream.php(154): _PHPStan_d3e3292d7\Evenement\EventEmitter->emit('data', Array)                  
#20 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(201): _PHPStan_d3e3292d7\React\Stream\DuplexResourceStream->handleData(Resource id    
#5961)                                                                                                                                                                                                                        
#21 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(173):                                                                                 
_PHPStan_d3e3292d7\React\EventLoop\StreamSelectLoop->waitForStreamActivity(NULL)                                                                                                                                              
#22 phar:///app/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(108): _PHPStan_d3e3292d7\React\EventLoop\StreamSelectLoop->run()                                         
#23 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259):                                                                                       
PHPStan\Command\WorkerCommand->execute(Object(_PHPStan_d3e3292d7\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_d3e3292d7\Symfony\Component\Console\Output\ConsoleOutput))                                       
#24 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870):                                                                                           
_PHPStan_d3e3292d7\Symfony\Component\Console\Command\Command->run(Object(_PHPStan_d3e3292d7\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_d3e3292d7\Symfony\Component\Console\Output\ConsoleOutput))            
#25 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261):                                                                                           
_PHPStan_d3e3292d7\Symfony\Component\Console\Application->doRunCommand(Object(PHPStan\Command\WorkerCommand), Object(_PHPStan_d3e3292d7\Symfony\Component\Console\Input\ArgvInput),                                           
Object(_PHPStan_d3e3292d7\Symfony\Component\Console\Output\ConsoleOutput))                                                                                                                                                    
#26 phar:///app/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157):                                                                                           
_PHPStan_d3e3292d7\Symfony\Component\Console\Application->doRun(Object(_PHPStan_d3e3292d7\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_d3e3292d7\Symfony\Component\Console\Output\ConsoleOutput))              
#27 phar:///app/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124): _PHPStan_d3e3292d7\Symfony\Component\Console\Application->run()                                                      
#28 phar:///app/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125): _PHPStan_d3e3292d7\{closure}()                                                                                       
#29 /app/vendor/phpstan/phpstan/phpstan(8): require('phar:///app...')                                                                                                                  
#30 /app/vendor/bin/phpstan(120): include('/app...')                                                                                                                            
#31 {main}                                                                                                                                                                                                                    

It seems like #427 might address this for us.

github-actions[bot] commented 1 year ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.