Skip to content

Commit

Permalink
Update to PHPStan 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-granados committed Dec 24, 2024
1 parent b62b26c commit c406baa
Show file tree
Hide file tree
Showing 35 changed files with 119 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/all_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ jobs:
fail-fast: false
matrix:
php-version:
- "8.0"
- "8.1"
- "8.2"
- "8.3"
- "8.4"

steps:
- name: "Checkout"
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ composer.lock
.phpunit.result.cache
.idea
cache
.codebase_patch_applied
.config_patch_applied
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ However, static analysis tools like Psalm have not made this transition to attri

This is a Psalm plugin that allows Psalm to understand a new set of attributes that replace the PHPDoc annotations. These attributes are defined in [this repository](https://github.com/php-static-analysis/attributes)

NOTE: Version 0.4.0 of this plugin requires Php Parser v5. The current available version of Psalm (v5) does not support this
version of the parser, so currently this library only supports the `dev-master` version of Psalm. If you need to
use Psalm 5, you will need to use version 0.3 of this plugin.

## Example

In order to show how code would look with these attributes, we can look at the following example. This is how a class looks like with the current annotations:
Expand Down Expand Up @@ -74,6 +78,12 @@ To use this plugin, require it in Composer:
composer require --dev php-static-analysis/psalm-plugin
```

NOTE: When adding this dependency, composer will ask you
if you want to allow this dependency as a composer plugin.
This is needed so that this plugin can patch Psalm in order
to enable its functionality. This will add an entry in your
`allow-plugins` composer config entry.

Then run this command to enable the plugin:

```
Expand Down
23 changes: 14 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "php-static-analysis/psalm-plugin",
"description": "Psalm plugin to read static analysis attributes",
"type": "psalm-plugin",
"type": "composer-plugin",
"keywords": ["dev", "static analysis"],
"license": "MIT",
"autoload": {
Expand All @@ -24,17 +24,19 @@
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=8.0",
"php": ">=8.1",
"composer-plugin-api": "^2.0",
"ext-simplexml": "*",
"php-static-analysis/attributes": "^0.3.1 || dev-main",
"php-static-analysis/node-visitor": "^0.3.1 || dev-main",
"vimeo/psalm": "^5",
"php-static-analysis/attributes": "^0.3.2 || dev-main",
"php-static-analysis/node-visitor": "^0.3.2 || dev-main",
"vimeo/psalm": "dev-master",
"webmozart/assert": "^1.11"
},
"require-dev": {
"php-static-analysis/phpstan-extension": "dev-main",
"composer/composer": "^2.0",
"php-static-analysis/phpstan-extension": "^0.3.2 || dev-main",
"phpstan/extension-installer": "^1.3",
"phpstan/phpstan": "^1.10",
"phpstan/phpstan": "^1.8 | ^2.0",
"phpunit/phpunit": "^9.0",
"symplify/easy-coding-standard": "^12.1"
},
Expand All @@ -50,7 +52,8 @@
"extra": {
"psalm": {
"pluginClass": "PhpStaticAnalysis\\PsalmPlugin\\Plugin"
}
},
"class": "PhpStaticAnalysis\\PsalmPlugin\\Composer\\Plugin"
},
"scripts": {
"tests": [
Expand All @@ -64,6 +67,8 @@
"ecs": "ecs",
"ecs-fix": "ecs --fix",
"phpunit": "phpunit",
"phpstan": "phpstan analyse"
"phpstan": "phpstan analyse",
"post-install-cmd": "PhpStaticAnalysis\\PsalmPlugin\\Composer\\Plugin::onPostInstall",
"post-update-cmd": "PhpStaticAnalysis\\PsalmPlugin\\Composer\\Plugin::onPostUpdate"
}
}
14 changes: 14 additions & 0 deletions patches/vimeo-psalm-src-psalm-codebase-php.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--- /dev/null
+++ ../src/Psalm/Codebase.php
@@ -131,7 +131,10 @@

public FileReferenceProvider $file_reference_provider;

- public StatementsProvider $statements_provider;
+ /**
+ * @var StatementsProvider
+ */
+ public $statements_provider;

private readonly Progress $progress;

11 changes: 11 additions & 0 deletions patches/vimeo-psalm-src-psalm-config-php.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--- /dev/null
+++ ../src/Psalm/Config.php
@@ -130,7 +130,7 @@
* @psalm-suppress PropertyNotSetInConstructor
* @psalm-consistent-constructor
*/
-final class Config
+class Config
{
final public const DEFAULT_BASELINE_NAME = 'psalm-baseline.xml';
private const DEFAULT_FILE_NAMES = [
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ parameters:
- src
- tests
excludePaths:
- tests/data/*
- tests/data/*
1 change: 0 additions & 1 deletion psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
<directory name="src" />
<directory name="tests" />
<ignoreFiles>
<directory name="vendor" />
<directory name="tests/data" />
</ignoreFiles>
</projectFiles>
Expand Down
61 changes: 61 additions & 0 deletions src/Composer/Plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace PhpStaticAnalysis\PsalmPlugin\Composer;

use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use Composer\Script\Event;
use PhpStaticAnalysis\Attributes\Returns;

class Plugin implements PluginInterface, EventSubscriberInterface
{
public function activate(Composer $composer, IOInterface $io)
{
}

public function deactivate(Composer $composer, IOInterface $io)
{
}

public function uninstall(Composer $composer, IOInterface $io)
{
}

#[Returns('array<string, string>')]
public static function getSubscribedEvents(): array
{
return [
'post-install-cmd' => 'onPostInstall',
'post-update-cmd' => 'onPostUpdate'
];
}

public static function onPostInstall(Event $event): void
{
self::applyPatches($event);
}

public static function onPostUpdate(Event $event): void
{
self::applyPatches($event);
}

private static function applyPatches(Event $event): void
{
/**
* @var string $vendorDir
*/
$vendorDir = $event->getComposer()->getConfig()->get('vendor-dir');

$dependencyPath = $vendorDir . '/vimeo/psalm';
$patchFile = __DIR__ . '/../../patches/vimeo-psalm-src-psalm-config-php.patch';

exec("patch -p1 -d $dependencyPath --forward < $patchFile");

$patchFile = __DIR__ . '/../../patches/vimeo-psalm-src-psalm-codebase-php.patch';

exec("patch -p1 -d $dependencyPath --forward < $patchFile");
}
}
2 changes: 1 addition & 1 deletion src/Provider/AttributeStatementProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function getStatementsForFile(
return $this->traverseAst($ast);
}

#[Param(args: 'mixed[]')]
#[Param(args: 'array<int|string, mixed>')]
public function __call(string $method, array $args): mixed
{
$callable = [$this->statementsProvider, $method];
Expand Down
2 changes: 1 addition & 1 deletion tests/AssertAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function testInvalidMethodAssertAttribute(): void
{
$errors = $this->analyzeTestFile('/data/Assert/InvalidMethodAssertAttribute.php');
$this->checkExpectedErrors($errors,[
'Argument 1 of PhpStaticAnalysis\Attributes\Assert::__construct expects string, but 0 provided' => 9,
'Misplaced variable in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Assert\InvalidMethodAssertAttribute::checkString' => 9,
'Attribute Assert cannot be used on a property' => 14,
]);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/AssertIfFalseAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function testInvalidMethodAssertIfFalseAttribute(): void
{
$errors = $this->analyzeTestFile('/data/AssertIfFalse/InvalidMethodAssertIfFalseAttribute.php');
$this->checkExpectedErrors($errors,[
'Argument 1 of PhpStaticAnalysis\Attributes\AssertIfFalse::__construct expects string, but 0 provided' => 9,
'Misplaced variable in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\AssertIfFalse\InvalidMethodAssertIfFalseAttribute::checkString' => 9,
'Attribute AssertIfFalse cannot be used on a property' => 15,
]);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/AssertIfTrueAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function testInvalidMethodAssertIfTrueAttribute(): void
{
$errors = $this->analyzeTestFile('/data/AssertIfTrue/InvalidMethodAssertIfTrueAttribute.php');
$this->checkExpectedErrors($errors,[
'Argument 1 of PhpStaticAnalysis\Attributes\AssertIfTrue::__construct expects string, but 0 provided' => 9,
'Misplaced variable in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\AssertIfTrue\InvalidMethodAssertIfTrueAttribute::checkString' => 9,
'Attribute AssertIfTrue cannot be used on a property' => 15,
]);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/BaseAttributeTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ protected function checkExpectedErrors(

$errorNum = 0;
foreach ($expectedErrors as $error => $line) {
/** @psalm-suppress InternalProperty */
/** @psalm-suppress InternalProperty, UndefinedPropertyFetch */
$this->assertSame($error, $errors[$errorNum]->message);
/** @psalm-suppress InternalProperty */
$this->assertSame($line, $errors[$errorNum]->line_from);
Expand Down
1 change: 0 additions & 1 deletion tests/DefineTypeAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public function testInvalidClassDefineTypeAttribute(): void

$expectedErrors = [
'Misplaced brackets' => 7,
'Argument 1 of PhpStaticAnalysis\Attributes\DefineType::__construct expects string, but 0 provided' => 7,
'Attribute DefineType cannot be used on a method' => 12,
];

Expand Down
1 change: 0 additions & 1 deletion tests/DeprecatedAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public function testInvalidMethodDeprecatedAttribute(): void

$expectedErrors = [
'Attribute Deprecated cannot be used on a function/method parameter' => 12,
'Attribute Deprecated is not repeatable' => 19,
];

$this->checkExpectedErrors($errors, $expectedErrors);
Expand Down
1 change: 0 additions & 1 deletion tests/ImmutableAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public function testInvalidClassImmutableAttribute(): void
$errors = $this->analyzeTestFile( '/data/Immutable/InvalidClassImmutableAttribute.php');

$expectedErrors = [
'Attribute Immutable is not repeatable' => 10,
'Attribute Immutable cannot be used on a property' => 13,
];

Expand Down
1 change: 0 additions & 1 deletion tests/ImportTypeAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public function testInvalidClassImportTypeAttribute(): void
$expectedErrors = [
'Invalid import in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\ImportType\InvalidClassImportTypeAttribute, expecting "<TypeName> from <ClassName>", got "" instead.' => 9,
'Invalid import in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\ImportType\InvalidClassImportTypeAttribute, expecting "<TypeName> from <ClassName>", got "string" instead.' => 10,
'Argument 1 of PhpStaticAnalysis\Attributes\ImportType::__construct expects string, but 0 provided' => 8,
'Attribute ImportType cannot be used on a method' => 13,
'Docblock-defined class, interface or enum named test\PhpStaticAnalysis\PsalmPlugin\data\ImportType\count($a) does not exist' => 11,
];
Expand Down
2 changes: 0 additions & 2 deletions tests/InternalAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ public function testInvalidMethodInternalAttribute(): void
$errors = $this->analyzeTestFile('/data/Internal/InvalidMethodInternalAttribute.php');
$expectedErrors = [
'psalm-internal annotation used without specifying namespace in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Internal\InvalidMethodInternalAttribute::getName' => 9,
'Argument 1 of PhpStaticAnalysis\Attributes\Internal::__construct expects null|string, but 0 provided' => 9,
'Attribute Internal cannot be used on a function/method parameter' => 15,
'Attribute Internal is not repeatable' => 22,
];

$this->checkExpectedErrors($errors, $expectedErrors);
Expand Down
2 changes: 0 additions & 2 deletions tests/IsReadOnlyAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ public function testInvalidPropertyIsReadOnlyAttribute(): void
$errors = $this->analyzeTestFile('/data/IsReadOnly/InvalidPropertyIsReadOnlyAttribute.php');
$this->checkExpectedErrors($errors,[
'Attribute IsReadOnly cannot be used on a method' => 16,
'Too many arguments for PhpStaticAnalysis\Attributes\IsReadOnly::__construct - expecting 0 but saw 1' => 9,
'Attribute IsReadOnly is not repeatable' => 13,
]);
}
}
1 change: 0 additions & 1 deletion tests/MethodAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public function testInvalidClassMethodAttribute(): void

$expectedErrors = [
'No @method entry specified in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Method\InvalidClassMethodAttribute' => 9,
'Argument 1 of PhpStaticAnalysis\Attributes\Method::__construct expects string, but 0 provided' => 8,
'Attribute Method cannot be used on a method' => 11,
'string is not a valid method in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Method\AnotherInvalidClassMethodAttribute' => 29,
];
Expand Down
1 change: 0 additions & 1 deletion tests/MixinAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public function testInvalidClassMixinAttribute(): void

$expectedErrors = [
'@mixin annotation used without specifying class in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Mixin\InvalidClassMixinAttribute' => 9,
'Argument 1 of PhpStaticAnalysis\Attributes\Mixin::__construct expects string, but 0 provided' => 7,
'Attribute Mixin cannot be used on a method' => 11,
];

Expand Down
1 change: 0 additions & 1 deletion tests/ParamAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public function testInvalidMethodParamAttribute(): void
$this->checkExpectedErrors($errors,[
'Badly-formatted @param in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Param\InvalidMethodParamAttribute::getNameLength' => 9,
'Badly-formatted @param in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Param\InvalidMethodParamAttribute::getOtherNameLength' => 15,
'Argument 1 of PhpStaticAnalysis\Attributes\Param::__construct expects string, but 0 provided' => 9,
'Misplaced brackets in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Param\InvalidMethodParamAttribute::getAnotherNameLength' => 22,
'Found duplicated @param or prefixed @param tag in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Param\InvalidMethodParamAttribute::countEvenMoreNames' => 30,
'Argument 1 of count cannot be mixed, expecting Countable|array<array-key, mixed>' => 33,
Expand Down
1 change: 0 additions & 1 deletion tests/ParamOutAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public function testInvalidMethodParamOutAttribute(): void
$expectedErrors = [
'Badly-formatted @param in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\ParamOut\InvalidMethodParamOutAttribute::setName' => 9,
'Badly-formatted @param in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\ParamOut\InvalidMethodParamOutAttribute::setOtherName' => 15,
'Argument 1 of PhpStaticAnalysis\Attributes\ParamOut::__construct expects string, but 0 provided' => 9,
'Misplaced brackets in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\ParamOut\InvalidMethodParamOutAttribute::setAnotherName' => 21,
'Attribute ParamOut cannot be used on a property' => 27,
];
Expand Down
1 change: 0 additions & 1 deletion tests/PropertyAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public function testInvalidClassPropertyAttribute(): void

$expectedErrors = [
'Badly-formatted @property in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Property\InvalidClassPropertyAttribute' => 8,
'Argument 1 of PhpStaticAnalysis\Attributes\Property::__construct expects string, but 0 provided' => 7,
'Attribute Property cannot be used on a method' => 10,
'Badly-formatted @property in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Property\AnotherInvalidClassPropertyAttribute' => 18,
'Misplaced brackets in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Property\AndAnotherInvalidClassPropertyAttribute' => 23,
Expand Down
1 change: 0 additions & 1 deletion tests/PropertyReadAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public function testInvalidClassPropertyReadAttribute(): void

$expectedErrors = [
'Badly-formatted @property in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\PropertyRead\InvalidClassPropertyReadAttribute' => 9,
'Argument 1 of PhpStaticAnalysis\Attributes\PropertyRead::__construct expects string, but 0 provided' => 7,
'Attribute PropertyRead cannot be used on a method' => 11,
'Badly-formatted @property in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\PropertyRead\AnotherInvalidClassPropertyReadAttribute' => 29,
'Misplaced brackets in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\PropertyRead\AndAnotherInvalidClassPropertyReadAttribute' => 34,
Expand Down
1 change: 0 additions & 1 deletion tests/PropertyWriteAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public function testInvalidClassPropertyWriteAttribute(): void
$expectedErrors = [
'Unable to determine the type that $foo is being assigned to' => 39,
'Badly-formatted @property in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\PropertyWrite\InvalidClassPropertyWriteAttribute' => 9,
'Argument 1 of PhpStaticAnalysis\Attributes\PropertyWrite::__construct expects string, but 0 provided' => 7,
'Attribute PropertyWrite cannot be used on a method' => 11,
'Badly-formatted @property in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\PropertyWrite\AnotherInvalidClassPropertyWriteAttribute' => 29,
'Misplaced brackets in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\PropertyWrite\AndAnotherInvalidClassPropertyWriteAttribute' => 34,
Expand Down
1 change: 0 additions & 1 deletion tests/PureAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public function testInvalidMethodPureAttribute(): void
$errors = $this->analyzeTestFile('/data/Pure/InvalidMethodPureAttribute.php');

$expectedErrors = [
'Attribute Pure is not repeatable' => 15,
'Attribute Pure cannot be used on a property' => 11,
];

Expand Down
3 changes: 0 additions & 3 deletions tests/ReturnsAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ public function testInvalidMethodReturnsAttribute(): void
{
$errors = $this->analyzeTestFile('/data/Returns/InvalidMethodReturnsAttribute.php');
$this->checkExpectedErrors($errors,[
'Argument 1 of PhpStaticAnalysis\Attributes\Returns::__construct expects string, but 0 provided' => 9,
'Found duplicated @return or prefixed @return tag in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Returns\InvalidMethodReturnsAttribute::getOtherName' => 15,
'Attribute Returns is not repeatable' => 16,
'Too many arguments for PhpStaticAnalysis\Attributes\Returns::__construct - expecting 1 but saw 2' => 22,
'Found duplicated @return or prefixed @return tag in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Returns\InvalidMethodReturnsAttribute::getSomeMoreNames' => 31,
'Misplaced variable in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Returns\InvalidMethodReturnsAttribute::getMoreAndMoreNames' => 37,
'Attribute Returns cannot be used on a property' => 43,
Expand Down
1 change: 0 additions & 1 deletion tests/SelfOutAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public function testInvalidMethodSelfOutAttribute(): void
$errors = $this->analyzeTestFile('/data/SelfOut/InvalidMethodSelfOutAttribute.php');

$expectedErrors = [
'Attribute SelfOut is not repeatable' => 15,
'Attribute SelfOut cannot be used on a property' => 20,
];

Expand Down
2 changes: 0 additions & 2 deletions tests/TemplateAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ public function testInvalidMethodTemplateAttribute(): void
$errors = $this->analyzeTestFile('/data/Template/InvalidMethodTemplateAttribute.php');
$this->checkExpectedErrors($errors,[
'Empty @template tag in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Template\InvalidMethodTemplateAttribute::getName' => 11,
'Argument 1 of PhpStaticAnalysis\Attributes\Template::__construct expects string, but 0 provided' => 11,
'Empty @template tag in docblock for test\PhpStaticAnalysis\PsalmPlugin\data\Template\InvalidMethodTemplateAttribute::getAnotherName' => 17,
'Argument 2 of PhpStaticAnalysis\Attributes\Template::__construct expects null|string, but 0 provided' => 26,
'Attribute Template cannot be used on a property' => 23,
]);
}
Expand Down
Loading

0 comments on commit c406baa

Please sign in to comment.