psalm / psalm-plugin-laravel

A Psalm plugin for Laravel
MIT License
307 stars 71 forks source link

PropertyNotSetInConstructor + NonInvariantDocblockPropertyType for new commands (`artisan make:command`), notifications (`artisan make:notification`) and requests (`artisan make:request`) #144

Closed caugner closed 3 years ago

caugner commented 3 years ago

Describe the bug

When I create a new command using artisan make:command, I get the following errors:

$ php artisan make:command Example
$ vendor/bin/psalm
ERROR: PropertyNotSetInConstructor - app/Console/Commands/Example.php:7:7 - Property App\Console\Commands\Example::$laravel is not defined in constructor of App\Console\Commands\Example and in any methods called in the constructor (see https://psalm.dev/074)
class Example extends Command

ERROR: PropertyNotSetInConstructor - app/Console/Commands/Example.php:7:7 - Property App\Console\Commands\Example::$name is not defined in constructor of App\Console\Commands\Example and in any methods called in the constructor (see https://psalm.dev/074)
class Example extends Command

ERROR: PropertyNotSetInConstructor - app/Console/Commands/Example.php:7:7 - Property App\Console\Commands\Example::$input is not defined in constructor of App\Console\Commands\Example and in any methods called in the constructor (see https://psalm.dev/074)
class Example extends Command

ERROR: PropertyNotSetInConstructor - app/Console/Commands/Example.php:7:7 - Property App\Console\Commands\Example::$output is not defined in constructor of App\Console\Commands\Example and in any methods called in the constructor (see https://psalm.dev/074)
class Example extends Command

ERROR: NonInvariantDocblockPropertyType - app/Console/Commands/Example.php:21:15 - Property App\Console\Commands\Example::$description has type string, not invariant with Illuminate\Console\Command::$description of type null|string (see https://psalm.dev/267)
    protected $description = 'Command description';

Impacted Versions

> composer show | grep -E 'psalm|laravel'
andrey-helldar/laravel-lang-publisher v9.2.1    Publisher lang files for the Laravel Framework, Jetstream, Fortify, Cashier and Nova from Laravel-Lang/lang
andrey-helldar/laravel-support        v2.0.1    Various helper files for the Laravel and Lumen frameworks
barryvdh/laravel-ide-helper           v2.9.1    Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.
beyondcode/laravel-dump-server        1.7.0     Symfony Var-Dump Server for Laravel
fruitcake/laravel-cors                v2.0.4    Adds CORS (Cross-Origin Resource Sharing) headers support in your Laravel application
laravel-lang/lang                     9.0.1     Languages for Laravel
laravel/framework                     v8.43.0   The Laravel Framework.
laravel/sanctum                       v2.11.1   Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.
laravel/tinker                        v2.6.1    Powerful REPL for the Laravel framework.
laravel/ui                            v3.2.1    Laravel UI utilities and presets.
psalm/plugin-laravel                  v1.4.5    A Laravel plugin for Psalm
sentry/sentry-laravel                 2.5.3     Laravel SDK for Sentry (https://sentry.io)
spatie/laravel-ray                    1.17.4    Easily debug Laravel apps
vimeo/psalm                           4.7.3     A static analysis tool for finding errors in PHP applications

Additional context For now, my workaround is

However, this is a bit cumbersome and especially frustrating for new developers.

caugner commented 3 years ago

As for the NonInvariantDocblockPropertyType error, this looks like a phpdoc bug in laravel/framework to me: description is never really null, because it is always set via setDescription() in Illuminate\Console\Command::__construct().

edit 1: The same holds for Command::$help, which is always set via setHelp().

In contrast, Command::$laravel is actually null on a newly constructed command object, before the property is set via setLaravel().

edit 2: Likewise, InteractsWithIO::$input and InteractsWithIO::$output are actually null (or more precisely: unset) until they are set via setInput() and setOutput().

Could we suppress the PropertyNotSetInConstructor for these properties via the laravel plugin?

mr-feek commented 3 years ago

Am I correct in understanding that you got this fixed upstream in laravel? Anything left to do here?

caugner commented 3 years ago

@mr-feek I was only able to fix the NonInvariantDocblockPropertyType error upstream, not the PropertyNotSetInConstructor errors. 😕

  1. To test this, could we run artisan make:command Example and assert that Psalm does not find any errors afterwards, or do we have to add such an Example[Command] as a stub (which is less flexible, becaus it doesn't take into account changes to the make:command command?
  2. To fix these errors, could we just add a stub for Illuminate\Console\Command that either (a) redefines those properties with @psalm-suppress PropertyNotSetInConstructor, or (b) defines them as typed properties so that they are either unset or values of the annotated type?
caugner commented 3 years ago

I just created my first FormRequest (php artisan make:request ExampleRequest) and it caused similar errors:

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$container is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$redirector is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$redirect is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$redirectRoute is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$redirectAction is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$validator is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$convertedFiles is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$userResolver is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$routeResolver is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$session is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest

ERROR: PropertyNotSetInConstructor - app/Http/Requests/ExampleRequest.php:7:7 - Property App\Http\Requests\ExampleRequest::$locale is not defined in constructor of App\Http\Requests\ExampleRequest and in any methods called in the constructor (see https://psalm.dev/074)
class ExampleRequest extends FormRequest
caugner commented 3 years ago

There are also PropertyNotSetInConstructor warnings when extending Illuminate\Foundation\Testing\TestCase:

psalm: PropertyNotSetInConstructor: Property Tests\Feature\ExampleTest::$app is not defined in constructor of Tests\Feature\ExampleTest or in any private or final methods called in the constructor
psalm: PropertyNotSetInConstructor: Property Tests\Feature\ExampleTest::$backupStaticAttributes is not defined in constructor of Tests\Feature\ExampleTest or in any private or final methods called in the constructor
psalm: PropertyNotSetInConstructor: Property Tests\Feature\ExampleTest::$callbackException is not defined in constructor of Tests\Feature\ExampleTest or in any private or final methods called in the constructor
psalm: PropertyNotSetInConstructor: Property Tests\Feature\ExampleTest::$runTestInSeparateProcess is not defined in constructor of Tests\Feature\ExampleTest or in any private or final methods called in the constructor
caugner commented 3 years ago

Notifications are also affected by this.