From ec6b4fcc38d4e0287d32f875b93acc55cb1e0547 Mon Sep 17 00:00:00 2001 From: Aleksander Kaim Date: Thu, 21 Nov 2024 16:50:03 +0100 Subject: [PATCH 1/4] PHPStan 2.0 --- .github/workflows/phpstan.yml | 3 ++ .github/workflows/tests-code-coverage.yml | 2 + .github/workflows/tests.yml | 4 ++ composer.json | 4 +- src/EnumDynamicReturnTypeExtension.php | 20 +++++---- src/EnumMethodsClassReflectionExtension.php | 1 - tests/integration/IntegrationTest.php | 13 ++---- .../data/EnumGetValuesReturnType-3.json | 15 +++++++ .../data/EnumGetValuesReturnType-7.json | 17 -------- .../data/EnumMethodsClassReflection-10.json | 7 ++++ .../EnumDynamicReturnTypeExtensionTest.php | 8 +--- tests/unit/EnumMethodReflectionTest.php | 42 +++++++++---------- 12 files changed, 72 insertions(+), 64 deletions(-) delete mode 100644 tests/integration/data/EnumGetValuesReturnType-7.json create mode 100644 tests/integration/data/EnumMethodsClassReflection-10.json diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 5b21f42..cbe06e8 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -18,6 +18,9 @@ jobs: php-version: - "7.4" - "8.0" + - "8.1" + - "8.2" + - "8.3" steps: - name: "Checkout" diff --git a/.github/workflows/tests-code-coverage.yml b/.github/workflows/tests-code-coverage.yml index 9985786..b0c785a 100644 --- a/.github/workflows/tests-code-coverage.yml +++ b/.github/workflows/tests-code-coverage.yml @@ -21,6 +21,8 @@ jobs: - "7.4" - "8.0" - "8.1" + - "8.2" + - "8.3" steps: - name: "Checkout" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6d29345..2f9e269 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,6 +17,10 @@ jobs: matrix: php-version: - "7.1" + - "8.0" + - "8.1" + - "8.2" + - "8.3" steps: - name: "Checkout" diff --git a/composer.json b/composer.json index 8515c66..a625848 100644 --- a/composer.json +++ b/composer.json @@ -5,9 +5,9 @@ "keywords": ["enum", "phpstan"], "license": "BSD-3-Clause", "require": { - "php": "^7.1 | ^8.0", + "php": "^7.4 | ^8.0", "marc-mabe/php-enum": "^1.1 || ^2.0 || ^3.0 || ^4.0", - "phpstan/phpstan": "^1.0" + "phpstan/phpstan": "^2.0" }, "require-dev": { "phpunit/phpunit": "^7.5 | ^8.5 | 9.4" diff --git a/src/EnumDynamicReturnTypeExtension.php b/src/EnumDynamicReturnTypeExtension.php index 8dc0226..3e8542f 100644 --- a/src/EnumDynamicReturnTypeExtension.php +++ b/src/EnumDynamicReturnTypeExtension.php @@ -50,11 +50,9 @@ public function __construct() return $this->detectGetValueReturnType($class); }; - if (method_exists(Enum::class, 'getvalues')) { - $this->staticMethods['getvalues'] = function (string $class) { - return $this->detectGetValuesReturnType($class); - }; - } + $this->staticMethods['getvalues'] = function (string $class) { + return $this->detectGetValuesReturnType($class); + }; // static methods can be called like object methods $this->objectMethods = array_merge($this->objectMethods, $this->staticMethods); @@ -87,7 +85,11 @@ public function getTypeFromStaticMethodCall( // The call class is not a name // E.g. an expression on $enumClass::getValues() if (!$callClass instanceof Name) { - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + return ParametersAcceptorSelector::selectFromArgs( + $scope, + $staticCall->getArgs(), + $methodReflection->getVariants() + )->getReturnType(); } $callClassName = $callClass->toString(); @@ -95,7 +97,11 @@ public function getTypeFromStaticMethodCall( // Can't detect possible types on static::*() // as it depends on defined enumerators of unknown inherited classes if ($callClassName === 'static') { - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + return ParametersAcceptorSelector::selectFromArgs( + $scope, + $staticCall->getArgs(), + $methodReflection->getVariants() + )->getReturnType(); } if ($callClassName === 'self') { diff --git a/src/EnumMethodsClassReflectionExtension.php b/src/EnumMethodsClassReflectionExtension.php index d078181..d653b5f 100644 --- a/src/EnumMethodsClassReflectionExtension.php +++ b/src/EnumMethodsClassReflectionExtension.php @@ -15,7 +15,6 @@ public function hasMethod(ClassReflection $classReflection, string $methodName): return false; } - /** @var string|Enum $class */ $class = $classReflection->getName(); return array_key_exists($methodName, $class::getConstants()); } diff --git a/tests/integration/IntegrationTest.php b/tests/integration/IntegrationTest.php index 125ed51..23ec1d8 100644 --- a/tests/integration/IntegrationTest.php +++ b/tests/integration/IntegrationTest.php @@ -1,4 +1,4 @@ - but returns array|bool|float|int|string|null>.", + "line": 21, + "ignorable": true + }, { "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::staticMethodFail() should return array but returns array.", "line": 40, @@ -14,9 +19,19 @@ "line": 70, "ignorable": true }, + { + "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyEnum::staticGetValuesFail() should return array but returns array|bool|float|int|string|null>.", + "line": 76, + "ignorable": true + }, { "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyInheritedEnum::inheritSelfGetValuesFail() should return array but returns array.", "line": 93, "ignorable": true + }, + { + "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyInheritedEnum::inheritStaticGetValuesFail() should return array but returns array|bool|float|int|string|null>.", + "line": 99, + "ignorable": true } ] \ No newline at end of file diff --git a/tests/integration/data/EnumGetValuesReturnType-7.json b/tests/integration/data/EnumGetValuesReturnType-7.json deleted file mode 100644 index a844752..0000000 --- a/tests/integration/data/EnumGetValuesReturnType-7.json +++ /dev/null @@ -1,17 +0,0 @@ -[ - { - "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::staticBaseExprMethodFail() should return array but returns array|bool|float|int|string|null>.", - "line": 21, - "ignorable": true - }, - { - "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyEnum::staticGetValuesFail() should return array but returns array|bool|float|int|string|null>.", - "line": 76, - "ignorable": true - }, - { - "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyInheritedEnum::inheritStaticGetValuesFail() should return array but returns array|bool|float|int|string|null>.", - "line": 99, - "ignorable": true - } -] \ No newline at end of file diff --git a/tests/integration/data/EnumMethodsClassReflection-10.json b/tests/integration/data/EnumMethodsClassReflection-10.json new file mode 100644 index 0000000..8c96f53 --- /dev/null +++ b/tests/integration/data/EnumMethodsClassReflection-10.json @@ -0,0 +1,7 @@ +[ + { + "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumMethodsClassReflection\\Example::fail() should return MabeEnum\\PHPStan\\tests\\integration\\data\\EnumMethodsClassReflection\\MyEnum but returns mixed.", + "line": 16, + "ignorable": true + } +] \ No newline at end of file diff --git a/tests/unit/EnumDynamicReturnTypeExtensionTest.php b/tests/unit/EnumDynamicReturnTypeExtensionTest.php index 32cd363..2d1698b 100644 --- a/tests/unit/EnumDynamicReturnTypeExtensionTest.php +++ b/tests/unit/EnumDynamicReturnTypeExtensionTest.php @@ -72,13 +72,7 @@ public function testIsMethodSupportedShouldReturnFalse(): void public function staticMethodsProvider(): array { - $staticMethods = []; - - if (method_exists(Enum::class, 'getValues')) { - $staticMethods[] = ['getValues']; - } - - return $staticMethods; + return [['getValues']]; } public function objectMethodsProvider(): array diff --git a/tests/unit/EnumMethodReflectionTest.php b/tests/unit/EnumMethodReflectionTest.php index 03f70dd..fa951c6 100644 --- a/tests/unit/EnumMethodReflectionTest.php +++ b/tests/unit/EnumMethodReflectionTest.php @@ -9,6 +9,7 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Testing\PHPStanTestCase; use PHPStan\Type\VerbosityLevel; +use PHPStan\Analyser\Scope; class EnumMethodReflectionTest extends PHPStanTestCase { @@ -30,7 +31,7 @@ public function setUp(): void public function testGetName(): void { - $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); + $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); $methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR'); $this->assertSame('STR', $methodReflection->getName()); @@ -38,7 +39,7 @@ public function testGetName(): void public function testGetDeclaringClass(): void { - $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); + $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); $methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR'); $this->assertSame($classReflection, $methodReflection->getDeclaringClass()); @@ -46,7 +47,7 @@ public function testGetDeclaringClass(): void public function testShouldBeStatic(): void { - $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); + $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); $methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR'); $this->assertTrue($methodReflection->isStatic()); @@ -54,7 +55,7 @@ public function testShouldBeStatic(): void public function testShouldNotBePrivate(): void { - $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); + $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); $methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR'); $this->assertFalse($methodReflection->isPrivate()); @@ -62,7 +63,7 @@ public function testShouldNotBePrivate(): void public function testShouldBePublic(): void { - $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); + $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); $methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR'); $this->assertTrue($methodReflection->isPublic()); @@ -70,7 +71,7 @@ public function testShouldBePublic(): void public function testGetPrototype(): void { - $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); + $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); $methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR'); $this->assertSame($methodReflection, $methodReflection->getPrototype()); @@ -78,11 +79,20 @@ public function testGetPrototype(): void public function testGetVariants(): void { - $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); + $classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class); $methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR'); - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); - $this->assertSame(VisibilityEnum::class, $parametersAcceptor->getReturnType()->describe(VerbosityLevel::value())); + $scope = $this->createMock(Scope::class); + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( + $scope, + [], + $methodReflection->getVariants() + ); + + $this->assertSame( + VisibilityEnum::class, + $parametersAcceptor->getReturnType()->describe(VerbosityLevel::value()) + ); } public function testGetDocComment(): void @@ -155,21 +165,11 @@ public function testHasSideEffects(): void public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void { - if (method_exists(parent::class, 'assertMatchesRegularExpression')) { - parent::assertMatchesRegularExpression($pattern, $string, $message); - return; - } - - self::assertRegExp($pattern, $string, $message); + parent::assertMatchesRegularExpression($pattern, $string, $message); } public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void { - if (method_exists(parent::class, 'assertDoesNotMatchRegularExpression')) { - parent::assertDoesNotMatchRegularExpression($pattern, $string, $message); - return; - } - - self::assertNotRegExp($pattern, $string, $message); + parent::assertDoesNotMatchRegularExpression($pattern, $string, $message); } } From 86a92ffb8a0b7a98d7251db4722b7c4372eaaeb9 Mon Sep 17 00:00:00 2001 From: Aleksander Kaim Date: Thu, 21 Nov 2024 16:53:01 +0100 Subject: [PATCH 2/4] Remove PHP 7.1 from github actions --- .github/workflows/tests-code-coverage.yml | 2 -- .github/workflows/tests.yml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/tests-code-coverage.yml b/.github/workflows/tests-code-coverage.yml index b0c785a..73cdb7b 100644 --- a/.github/workflows/tests-code-coverage.yml +++ b/.github/workflows/tests-code-coverage.yml @@ -16,8 +16,6 @@ jobs: fail-fast: false matrix: php-version: - - "7.2" - - "7.3" - "7.4" - "8.0" - "8.1" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2f9e269..44e85a6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: php-version: - - "7.1" + - "7.4" - "8.0" - "8.1" - "8.2" From 200b886d200202a875c7aac1118d6312133d26aa Mon Sep 17 00:00:00 2001 From: Aleksander Kaim Date: Thu, 28 Nov 2024 11:10:23 +0100 Subject: [PATCH 3/4] remove old version mabe enum support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a625848..191275b 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "license": "BSD-3-Clause", "require": { "php": "^7.4 | ^8.0", - "marc-mabe/php-enum": "^1.1 || ^2.0 || ^3.0 || ^4.0", + "marc-mabe/php-enum": "^4.0", "phpstan/phpstan": "^2.0" }, "require-dev": { From ec3d48309f3da4add7909a8ec1e07baa58a322a7 Mon Sep 17 00:00:00 2001 From: Aleksander Kaim Date: Thu, 28 Nov 2024 14:16:33 +0100 Subject: [PATCH 4/4] drop support for old phpunit & fix github actions --- .github/workflows/phpstan.yml | 1 + .github/workflows/tests-code-coverage.yml | 3 --- .github/workflows/tests.yml | 3 +-- composer.json | 2 +- src/EnumDynamicReturnTypeExtension.php | 8 +++---- src/EnumMethodReflection.php | 12 +++------- tests/integration/IntegrationTest.php | 2 -- .../EnumDynamicReturnTypeExtensionTest.php | 5 +---- tests/unit/EnumMethodReflectionTest.php | 22 +++---------------- ...numMethodsClassReflectionExtensionTest.php | 12 +++------- 10 files changed, 17 insertions(+), 53 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index cbe06e8..aebaaa3 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -21,6 +21,7 @@ jobs: - "8.1" - "8.2" - "8.3" + - "8.4" steps: - name: "Checkout" diff --git a/.github/workflows/tests-code-coverage.yml b/.github/workflows/tests-code-coverage.yml index 73cdb7b..960be7c 100644 --- a/.github/workflows/tests-code-coverage.yml +++ b/.github/workflows/tests-code-coverage.yml @@ -18,9 +18,6 @@ jobs: php-version: - "7.4" - "8.0" - - "8.1" - - "8.2" - - "8.3" steps: - name: "Checkout" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 44e85a6..2866175 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,11 +16,10 @@ jobs: fail-fast: false matrix: php-version: - - "7.4" - - "8.0" - "8.1" - "8.2" - "8.3" + - "8.4" steps: - name: "Checkout" diff --git a/composer.json b/composer.json index 191275b..903e455 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "phpstan/phpstan": "^2.0" }, "require-dev": { - "phpunit/phpunit": "^7.5 | ^8.5 | 9.4" + "phpunit/phpunit": "^9.0" }, "autoload": { "psr-4": { diff --git a/src/EnumDynamicReturnTypeExtension.php b/src/EnumDynamicReturnTypeExtension.php index 3e8542f..86b3237 100644 --- a/src/EnumDynamicReturnTypeExtension.php +++ b/src/EnumDynamicReturnTypeExtension.php @@ -23,26 +23,26 @@ class EnumDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExt * * @var array */ - private $objectMethods = []; + private array $objectMethods = []; /** * Map supported static method to a callable function detecting return type * * @var array */ - private $staticMethods = []; + private array $staticMethods = []; /** * Buffer of all types of enumeration values * @phpstan-var array, Type[]> */ - private $enumValueTypesBuffer = []; + private array $enumValueTypesBuffer = []; /** * Buffer of all types of enumeration ordinals * @phpstan-var array, Type[]> */ - private $enumOrdinalTypesBuffer = []; + private array $enumOrdinalTypesBuffer = []; public function __construct() { diff --git a/src/EnumMethodReflection.php b/src/EnumMethodReflection.php index 8a02d1d..db8c6e3 100644 --- a/src/EnumMethodReflection.php +++ b/src/EnumMethodReflection.php @@ -13,15 +13,9 @@ class EnumMethodReflection implements MethodReflection { - /** - * @var ClassReflection - */ - private $classReflection; - - /** - * @var string - */ - private $name; + private ClassReflection $classReflection; + + private string $name; public function __construct(ClassReflection $classReflection, string $name) { diff --git a/tests/integration/IntegrationTest.php b/tests/integration/IntegrationTest.php index 23ec1d8..4ff5ebc 100644 --- a/tests/integration/IntegrationTest.php +++ b/tests/integration/IntegrationTest.php @@ -2,12 +2,10 @@ namespace MabeEnum\PHPStan\tests\integration; -use MabeEnum\Enum; use PHPStan\Testing\LevelsTestCase; final class IntegrationTest extends LevelsTestCase { - /** * @return string[][] */ diff --git a/tests/unit/EnumDynamicReturnTypeExtensionTest.php b/tests/unit/EnumDynamicReturnTypeExtensionTest.php index 2d1698b..dd4ecf2 100644 --- a/tests/unit/EnumDynamicReturnTypeExtensionTest.php +++ b/tests/unit/EnumDynamicReturnTypeExtensionTest.php @@ -9,10 +9,7 @@ class EnumDynamicReturnTypeExtensionTest extends PHPStanTestCase { - /** - * @var EnumDynamicReturnTypeExtension - */ - protected $extension; + protected EnumDynamicReturnTypeExtension $extension; public function setUp(): void { diff --git a/tests/unit/EnumMethodReflectionTest.php b/tests/unit/EnumMethodReflectionTest.php index fa951c6..7fc042b 100644 --- a/tests/unit/EnumMethodReflectionTest.php +++ b/tests/unit/EnumMethodReflectionTest.php @@ -7,21 +7,15 @@ use MabeEnum\PHPStan\tests\assets\DocCommentEnum; use MabeEnum\PHPStan\tests\assets\VisibilityEnum; use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\Reflection\ReflectionProvider; use PHPStan\Testing\PHPStanTestCase; use PHPStan\Type\VerbosityLevel; use PHPStan\Analyser\Scope; class EnumMethodReflectionTest extends PHPStanTestCase { - /** - * @var \PHPStan\Reflection\ReflectionProvider - */ - protected $reflectionProvider; - - /** - * @var EnumMethodsClassReflectionExtension - */ - protected $reflectionExtension; + protected ReflectionProvider $reflectionProvider; + protected EnumMethodsClassReflectionExtension $reflectionExtension; public function setUp(): void { @@ -162,14 +156,4 @@ public function testHasSideEffects(): void $this->assertTrue($methodReflection->hasSideEffects()->no()); } - - public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void - { - parent::assertMatchesRegularExpression($pattern, $string, $message); - } - - public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void - { - parent::assertDoesNotMatchRegularExpression($pattern, $string, $message); - } } diff --git a/tests/unit/EnumMethodsClassReflectionExtensionTest.php b/tests/unit/EnumMethodsClassReflectionExtensionTest.php index 121381d..fcc848c 100644 --- a/tests/unit/EnumMethodsClassReflectionExtensionTest.php +++ b/tests/unit/EnumMethodsClassReflectionExtensionTest.php @@ -6,19 +6,13 @@ use MabeEnumPHPStan\EnumMethodsClassReflectionExtension; use MabeEnum\PHPStan\tests\assets\NotAnEnum; use MabeEnum\PHPStan\tests\assets\VisibilityEnum; +use PHPStan\Reflection\ReflectionProvider; use PHPStan\Testing\PHPStanTestCase; class EnumMethodsClassReflectionExtensionTest extends PHPStanTestCase { - /** - * @var \PHPStan\Reflection\ReflectionProvider - */ - protected $reflectionProvider; - - /** - * @var EnumMethodsClassReflectionExtension - */ - protected $extension; + protected ReflectionProvider $reflectionProvider; + protected EnumMethodsClassReflectionExtension $extension; public function setUp(): void {