TomasVotruba / bladestan

PHPStan analysis for Blade templates
https://tomasvotruba.com/blog/introducing-bladestan-phpstan-analysis-of-blade-templates/
MIT License
298 stars 13 forks source link

Should not happen...happens #92

Open calebdw opened 11 months ago

calebdw commented 11 months ago

Hello!

I'm trying to install and use this extension but I'm getting several of the below errors when trying to run PHPStan (level 1) on my codebase:

Uncaught TomasVotruba\Bladestan\Exception\ShouldNotHappenException:  in /var/www/vendor/tomasvotruba/bladestan/src/PhpParser/NodeVisitor/ViewFunctionArgumentsNodeVisitor.php:77
#0 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(113): TomasVotruba\Bladestan\PhpParser\NodeVisitor\ViewFunctionArgumentsNodeVisitor->enterNode(Object(PhpParser\Node\Expr\MethodCall))
#1 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(133): PhpParser\NodeTraverser->traverseNode(Object(PhpParser\Node\Expr\MethodCall))
#2 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(133): PhpParser\NodeTraverser->traverseNode(Object(PhpParser\Node\Expr\Assign))
#3 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(196): PhpParser\NodeTraverser->traverseNode(Object(PhpParser\Node\Stmt\Expression))
#4 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(105): PhpParser\NodeTraverser->traverseArray(Array)
#5 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(196): PhpParser\NodeTraverser->traverseNode(Object(PhpParser\Node\Stmt\Foreach_))
#6 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(105): PhpParser\NodeTraverser->traverseArray(Array)
#7 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(196): PhpParser\NodeTraverser->traverseNode(Object(PhpParser\Node\Stmt\ClassMethod))
#8 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(105): PhpParser\NodeTraverser->traverseArray(Array)
#9 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(196): PhpParser\NodeTraverser->traverseNode(Object(PhpParser\Node\Stmt\Trait_))
#10 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(105): PhpParser\NodeTraverser->traverseArray(Array)
#11 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(196): PhpParser\NodeTraverser->traverseNode(Object(PhpParser\Node\Stmt\Namespace_))
#12 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(85): PhpParser\NodeTraverser->traverseArray(Array)
#13 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Parser/RichParser.php(81): PhpParser\NodeTraverser->traverse(Array)
#14 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Parser/RichParser.php(54): PHPStan\Parser\RichParser->parseString('<?php\n\nnamespac...')
#15 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Parser/PathRoutingParser.php(56): PHPStan\Parser\RichParser->parseFile('/var/www/eagle/...')
#16 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Parser/CachedParser.php(44): PHPStan\Parser\PathRoutingParser->parseFile('/var/www/eagle/...')
#17 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Reflection/BetterReflection/SourceLocator/FileNodesFetcher.php(31): PHPStan\Parser\CachedParser->parseFile('/var/www/eagle/...')
#18 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Reflection/BetterReflection/SourceLocator/NewOptimizedDirectorySourceLocator.php(57): PHPStan\Reflection\BetterReflection\SourceLocator\FileNodesFetcher->fetchNodes('/var/www/eagle/...')
#19 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/ondrejmirtes/better-reflection/src/SourceLocator/Type/AggregateSourceLocator.php(26): PHPStan\Reflection\BetterReflection\SourceLocator\NewOptimizedDirectorySourceLocator->locateIdentifier(Object(PHPStan\BetterReflection\Reflector\DefaultReflector), Object(PHPStan\BetterReflection\Identifier\Identifier))
#20 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Reflection/BetterReflection/SourceLocator/RewriteClassAliasSourceLocator.php(32): PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator->locateIdentifier(Object(PHPStan\BetterReflection\Reflector\DefaultReflector), Object(PHPStan\BetterReflection\Identifier\Identifier))
#21 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/ondrejmirtes/better-reflection/src/SourceLocator/Type/AggregateSourceLocator.php(26): PHPStan\Reflection\BetterReflection\SourceLocator\RewriteClassAliasSourceLocator->locateIdentifier(Object(PHPStan\BetterReflection\Reflector\DefaultReflector), Object(PHPStan\BetterReflection\Identifier\Identifier))
#22 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/ondrejmirtes/better-reflection/src/SourceLocator/Type/MemoizingSourceLocator.php(33): PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator->locateIdentifier(Object(PHPStan\BetterReflection\Reflector\DefaultReflector), Object(PHPStan\BetterReflection\Identifier\Identifier))
#23 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/ondrejmirtes/better-reflection/src/Reflector/DefaultReflector.php(32): PHPStan\BetterReflection\SourceLocator\Type\MemoizingSourceLocator->locateIdentifier(Object(PHPStan\BetterReflection\Reflector\DefaultReflector), Object(PHPStan\BetterReflection\Identifier\Identifier))
#24 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionClass.php(1025): PHPStan\BetterReflection\Reflector\DefaultReflector->reflectClass('Eagle\\File\\Rela...')
#25 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionClass.php(360): PHPStan\BetterReflection\Reflection\ReflectionClass->getTraits()
#26 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Reflection/ClassReflection.php(851): PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass->getTraits()
#27 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Dependency/DependencyResolver.php(448): PHPStan\Reflection\ClassReflection->getTraits()
#28 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Dependency/DependencyResolver.php(313): PHPStan\Dependency\DependencyResolver->addClassToDependencies('App\\Settings\\Mo...', Array)
#29 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(149): PHPStan\Dependency\DependencyResolver->resolveDependencies(Object(PhpParser\Node\Expr\ClassConstFetch), Object(PHPStan\Analyser\MutatingScope))
#30 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Node/ClassStatementsGatherer.php(108): PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}(Object(PhpParser\Node\Expr\ClassConstFetch), Object(PHPStan\Analyser\MutatingScope))
#31 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(2674): PHPStan\Node\ClassStatementsGatherer->__invoke(Object(PhpParser\Node\Expr\ClassConstFetch), Object(PHPStan\Analyser\MutatingScope))
#32 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(1606): PHPStan\Analyser\NodeScopeResolver->callNodeCallbackWithExpression(Object(PHPStan\Node\ClassStatementsGatherer), Object(PhpParser\Node\Expr\ClassConstFetch), Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Analyser\ExpressionContext))
#33 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(701): PHPStan\Analyser\NodeScopeResolver->processExprNode(Object(PhpParser\Node\Expr\ClassConstFetch), Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Node\ClassStatementsGatherer), Object(PHPStan\Analyser\ExpressionContext))
#34 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(680): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\PropertyProperty), Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Node\ClassStatementsGatherer), Object(PHPStan\Analyser\StatementContext))
#35 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(402): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Property), Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Node\ClassStatementsGatherer), Object(PHPStan\Analyser\StatementContext))
#36 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(669): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Class_), Array, Object(PHPStan\Analyser\MutatingScope), Object(PHPStan\Node\ClassStatementsGatherer), Object(PHPStan\Analyser\StatementContext))
#37 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(402): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Class_), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#38 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(641): PHPStan\Analyser\NodeScopeResolver->processStmtNodes(Object(PhpParser\Node\Stmt\Namespace_), Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#39 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(371): PHPStan\Analyser\NodeScopeResolver->processStmtNode(Object(PhpParser\Node\Stmt\Namespace_), Object(PHPStan\Analyser\MutatingScope), Object(Closure), Object(PHPStan\Analyser\StatementContext))
#40 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(166): PHPStan\Analyser\NodeScopeResolver->processNodes(Array, Object(PHPStan\Analyser\MutatingScope), Object(Closure))
#41 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/Analyser.php(72): PHPStan\Analyser\FileAnalyser->analyseFile('/var/www/app/Se...', Array, Object(PHPStan\Rules\LazyRegistry), Object(PHPStan\Collectors\Registry), NULL)
#42 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyserRunner.php(62): PHPStan\Analyser\Analyser->analyse(Array, Object(Closure), NULL, true, Array)
#43 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyseApplication.php(209): PHPStan\Command\AnalyserRunner->runAnalyser(Array, Array, Object(Closure), NULL, true, true, '/var/www/phpsta...', Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput))
#44 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyseApplication.php(101): PHPStan\Command\AnalyseApplication->runAnalyser(Array, Array, true, '/var/www/phpsta...', Object(PHPStan\Command\Symfony\SymfonyOutput), Object(PHPStan\Command\Symfony\SymfonyOutput), Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput))
#45 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyseCommand.php(198): PHPStan\Command\AnalyseApplication->analyse(Array, false, Object(PHPStan\Command\Symfony\SymfonyOutput), Object(PHPStan\Command\Symfony\SymfonyOutput), false, true, '/var/www/phpsta...', Array, Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput))
#46 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259): PHPStan\Command\AnalyseCommand->execute(Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_39fe102d2\Symfony\Component\Console\Output\ConsoleOutput))
#47 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870): _PHPStan_39fe102d2\Symfony\Component\Console\Command\Command->run(Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_39fe102d2\Symfony\Component\Console\Output\ConsoleOutput))
#48 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261): _PHPStan_39fe102d2\Symfony\Component\Console\Application->doRunCommand(Object(PHPStan\Command\AnalyseCommand), Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_39fe102d2\Symfony\Component\Console\Output\ConsoleOutput))
#49 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157): _PHPStan_39fe102d2\Symfony\Component\Console\Application->doRun(Object(_PHPStan_39fe102d2\Symfony\Component\Console\Input\ArgvInput), Object(_PHPStan_39fe102d2\Symfony\Component\Console\Output\ConsoleOutput))
#50 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124): _PHPStan_39fe102d2\Symfony\Component\Console\Application->run()
#51 phar:///var/www/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125): _PHPStan_39fe102d2\{closure}()
#52 /var/www/vendor/phpstan/phpstan/phpstan(8): require('phar:///var/www...')
#53 /var/www/vendor/bin/phpstan(119): include('/var/www/vendor...')
#54 {main}

Thanks!

AJenbo commented 11 months ago

Hi, it's hard to debug with out knowing the code that triggers this. Can you please provide an example of how your code calls the view() method? Extra points for giving a minimal testcase that can replicate the issue.

For reference this is where the analyzer dies: https://github.com/TomasVotruba/bladestan/blob/c8f99c508772e2d872d86558a0c426388c4dc7dd/src/PhpParser/NodeVisitor/ViewFunctionArgumentsNodeVisitor.php#L77 If you can modify that to output the class of $rootViewNode->name that could also be helpful.

My best guess from looking at the stack trace is that the code looks something like this:

<?php

namespace Eagle\File\Relation;

use App\Settings\Model\SomeSetting, but that's just a guess;

trait Something
{
    public somthing()
    {
        new SomeSetting();
        foreach () {
            $thing = view();
        }
    }
}

I took a look at https://github.com/eagle-metal, but it does not appear that the offending code is part of the publicly available repos.

You can run phpstan with --debug to have it print out what file it is currently working on, that should let you know what it's choking on.

calebdw commented 11 months ago

@AJenbo, thanks for the response.

Hi, it's hard to debug with out knowing the code that triggers this

I completely understand, however, this was produced with --debug and -vvv and the message yielded is not very helpful---the error output could certainly be improved.

I'm not even sure what file caused the error as the last filename on the list does not contain a view command and passes when I analyze it individually. I suspect it's choking on some View composers (which comes right after the Settings model you mentioned)

AJenbo commented 11 months ago

It's hard to say, it could be interactions between other PHPStan plugins so it's not really easy to say without a test case. Hopefully you can work on narrowing down the code that triggers it.

calebdw commented 11 months ago

@AJenbo, here's a minimum example (although there could be other issues not represented here):

<?php

namespace App\View\Components;

use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class Messages extends Component
{
    public function __construct(
        public string $resourceRoute,
        public array $messages = [],
        public ?string $fileMorphAlias = null,
        public bool $canCreateMessage = false,
    ) {
    }

    public function render(): View
    {
        return view('ems._components.messages');
    }
}
@php
    use App\Mail\Models\Message;
@endphp

<div class="mr-6">
    <span>
        <x-button.success
            class="text-sm"
            x-show="selected_message?.type === {{ Js::from(Message::TYPE_OUTGOING) }}"
        >
            Resend message
        </x-button.success>
    </span>

    <div class="mr-6">
        @isset($additional_actions)
            {{ $additional_actions }}
        @endisset
    </div>
</div>

Note that if I remove the x-show line or the isset block then the issue goes away---although they seen completely unrelated to each other

AJenbo commented 11 months ago

Could you try this:

<?php

namespace App\View\Components;

use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class Messages extends Component
{
    public function __construct(
        public string $resourceRoute,
        public array $messages = [],
        public ?string $fileMorphAlias = null,
        public bool $canCreateMessage = false,
    ) {
    }

    public function render(): View
    {
        return $this->view('ems._components.messages', [        
            'resourceRoute' => $this->resourceRoute,
            'messages' => $this->messages,
            'fileMorphAlias' => $this->fileMorphAlias,
            'canCreateMessage' => $this->canCreateMessage,
        ]);
    }
}

Bladestan isn't able to infer that the component and it's properties will be grabbed from the scope of the caller when calls happen inside a component. Also calling the member method gives it a slightly better context.

AJenbo commented 11 months ago

Is the blade file you posted ems/_components/messages.blade.php? Also we are missing button.blade.php since it's being required by the template you posted.

calebdw commented 11 months ago

@AJenbo, no go...

Here's the output of dd($rootViewNode)

/var/www/app/View/Components/Messages.php
PhpParser\Node\Expr\MethodCall^ {#56669
  #attributes: array:6 [
    "startLine" => 123
    "startTokenPos" => 789
    "startFilePos" => 3609
    "endLine" => 123
    "endTokenPos" => 795
    "endFilePos" => 3632
  ]
  +var: PhpParser\Node\Expr\Variable^ {#56667
    #attributes: array:6 [
      "startLine" => 123
      "startTokenPos" => 789
      "startFilePos" => 3609
      "endLine" => 123
      "endTokenPos" => 789
      "endFilePos" => 3613
    ]
    +name: "this"
  }
  +name: PhpParser\Node\Expr\Variable^ {#56668
    #attributes: array:6 [
      "startLine" => 123
      "startTokenPos" => 792
      "startFilePos" => 3617
      "endLine" => 123
      "endTokenPos" => 792
      "endFilePos" => 3629
    ]
    +name: "relationship"
  }
  +args: []
} // vendor/tomasvotruba/bladestan/src/PhpParser/NodeVisitor/ViewFunctionArgumentsNodeVisitor.php:77
calebdw commented 11 months ago

@AJenbo

Also we are missing button.blade.php since it's being required by the template you posted.

this is not relevant to the error, the template I posted has been trimmed down while still obtaining the error---you can just substitute a button element

calebdw commented 11 months ago

@AJenbo,

Here's another MRE:

<?php

namespace App\View\Components\Maintenance\System;

use Eagle\Maintenance\Models\MaintenanceSystem;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
use Illuminate\View\Component;

class DropdownTree extends Component
{
    public function __construct(
        /** @var Collection<int, MaintenanceSystem> */
        public Collection $systems,
        public bool $onlyActive = true,
        public ?MaintenanceSystem $selectedSystem = null,
    ) {
    }

    public function render(): View
    {
        return view('ems.maintenance.system._components.dropdown-tree');
    }
}
<div>
</div>

Note that I've removed the entire blade contents and the failure still occurs---it's not until I remove all the properties from the class that phpstan passes:

<?php

namespace App\View\Components\Maintenance\System;

use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class DropdownTree extends Component
{
    public function __construct(
    ) {
    }

    public function render(): View
    {
        return view('ems.maintenance.system._components.dropdown-tree');
    }
}
AJenbo commented 11 months ago

You don't need to @ me I'm active in the topic πŸ™‚

AJenbo commented 11 months ago

I noticed that you are using promoted constructors, could you test if that is what is causing the issue?

Scratch that I have some components like that as well and they do not cause any issues.

AJenbo commented 11 months ago

Here's another MRE:

<?php

namespace App\View\Components\Maintenance\System;

use Eagle\Maintenance\Models\MaintenanceSystem;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
use Illuminate\View\Component;

class DropdownTree extends Component
{
    public function __construct(
        /** @var Collection<int, MaintenanceSystem> */
        public Collection $systems,
        public bool $onlyActive = true,
        public ?MaintenanceSystem $selectedSystem = null,
    ) {
    }

    public function render(): View
    {
        return view('ems.maintenance.system._components.dropdown-tree');
    }
}
<div>
</div>

Sorry but this is not enough to replicate the issue (I tried adding them to another repo and analyzing them). Like I mentioned it could be interactions between other PHPStan plugins you are using, or some other aspect of your repo. Please provide a project or steps to replicate the error.

$ vendor/bin/phpstan analyze app/View/Components/Maintenance/System/DropdownTree.php --level 1
Note: Using configuration file phpstan.neon.
 1/1 [β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“] 100%

 ------ --------------------------------------------------------------------------------------------------------------------------------------------------------- 
  Line   DropdownTree.php                                                                                                                                         
 ------ --------------------------------------------------------------------------------------------------------------------------------------------------------- 
  16     Parameter $selectedSystem of method App\View\Components\Maintenance\System\DropdownTree::__construct() has invalid type                                  
         Eagle\Maintenance\Models\MaintenanceSystem.                                                                                                              
  16     Property App\View\Components\Maintenance\System\DropdownTree::$selectedSystem has unknown class Eagle\Maintenance\Models\MaintenanceSystem as its type.  
         πŸ’‘ Learn more at https://phpstan.org/user-guide/discovering-symbols                                                                                      
 ------ --------------------------------------------------------------------------------------------------------------------------------------------------------- 

Maybe the issue is with MaintenanceSystem?

Swapping it for a ransom model gives me a [OK] No errors all the way to level 9.

AJenbo commented 11 months ago

Have you tried removing $systems and $selectedSystem, but not $onlyActive?

calebdw commented 11 months ago

I've created an example repo here: https://github.com/calebdw/bladestan-errors

I've narrowed the issue down to two completely unrelated things that don't cause the failure when either of them are missing---not sure why their combined presence causes the failure. You're right about the issue being on the model---specifically with the $selectedSystem property in the DropdownTree component:

The two interactions are:

  1. @property-read annotation above the MaintenanceSystem model
  2. HasFiles::restoreAllFiles() trait method

The MaintenanceSystem is a recursive model and therefore uses the laravel-adjacency-list package for the recursive relationships. The @property-read annotation is required as Larastan is not able to deduce type for the $ancestorsAndSelf property relation.

We also have a custom Files relation that can be added to a model through the HasFiles trait. Bladestan is failing in the HasFiles::restoreAllFiles() method, specifically with the $this->{$relationship}() dynamic access:

    public function restoreAllFiles(): void
    {
        foreach ($this->fileRelationships as $relationship) {
            $loaded = $this->{$relationship}()->withTrashed()->get();

            foreach ($loaded as $relation) {
                $relation->restore();
            }
        }
    }

however, there are other methods in the same trait with similar access that don't give Bladestan any issues---Bladestan should not be trying to analyze these methods in the first place though. For some reason, Bladestan is trying to analyze the HasFiles::restoreAllFiles() method only when the @property-read Collection<int, self> $ancestorsAndSelf annotation is on the model.

williamdes commented 2 months ago

Maybe this issue is solved now ?

AJenbo commented 2 months ago

@williamdes Someone confirmed it last week. Did you test with the example repo for triggering the issue?