Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
php-version:
- "7.4"
- "8.0"
- "8.1"
- "8.2"
- "8.3"

steps:
- name: "Checkout"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests-code-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ jobs:
fail-fast: false
matrix:
php-version:
- "7.2"
- "7.3"
- "7.4"
- "8.0"
- "8.1"
- "8.2"
- "8.3"

steps:
- name: "Checkout"
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ jobs:
fail-fast: false
matrix:
php-version:
- "7.1"
- "7.4"
- "8.0"
- "8.1"
- "8.2"
- "8.3"

steps:
- name: "Checkout"
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
20 changes: 13 additions & 7 deletions src/EnumDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -87,15 +85,23 @@ 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();

// 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') {
Expand Down
1 change: 0 additions & 1 deletion src/EnumMethodsClassReflectionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
13 changes: 4 additions & 9 deletions tests/integration/IntegrationTest.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php declare(strict_types = 1);
<?php declare(strict_types=1);

namespace MabeEnum\PHPStan\tests\integration;

Expand All @@ -11,18 +11,13 @@ final class IntegrationTest extends LevelsTestCase
/**
* @return string[][]
*/
public function dataTopics(): array
public static function dataTopics(): array
{
$dataTopics = [
return [
['EnumMethodsClassReflection'],
['EnumGetValueReturnType'],
['EnumGetValuesReturnType'],
];

if (method_exists(Enum::class, 'getValues')) {
$dataTopics[] = ['EnumGetValuesReturnType'];
}

return $dataTopics;
}

public function getDataPath(): string
Expand Down
15 changes: 15 additions & 0 deletions tests/integration/data/EnumGetValuesReturnType-3.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
[
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::staticBaseExprMethodFail() should return array<int, null> but returns array<int, array<int|string, mixed>|bool|float|int|string|null>.",
"line": 21,
"ignorable": true
},
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::staticMethodFail() should return array<int, null> but returns array<int, float|int|string>.",
"line": 40,
Expand All @@ -14,9 +19,19 @@
"line": 70,
"ignorable": true
},
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyEnum::staticGetValuesFail() should return array<int, null> but returns array<int, array<int|string, mixed>|bool|float|int|string|null>.",
"line": 76,
"ignorable": true
},
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyInheritedEnum::inheritSelfGetValuesFail() should return array<int, null> but returns array<int, float|int|string>.",
"line": 93,
"ignorable": true
},
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyInheritedEnum::inheritStaticGetValuesFail() should return array<int, null> but returns array<int, array<int|string, mixed>|bool|float|int|string|null>.",
"line": 99,
"ignorable": true
}
]
17 changes: 0 additions & 17 deletions tests/integration/data/EnumGetValuesReturnType-7.json

This file was deleted.

7 changes: 7 additions & 0 deletions tests/integration/data/EnumMethodsClassReflection-10.json
Original file line number Diff line number Diff line change
@@ -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
}
]
8 changes: 1 addition & 7 deletions tests/unit/EnumDynamicReturnTypeExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 21 additions & 21 deletions tests/unit/EnumMethodReflectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Testing\PHPStanTestCase;
use PHPStan\Type\VerbosityLevel;
use PHPStan\Analyser\Scope;

class EnumMethodReflectionTest extends PHPStanTestCase
{
Expand All @@ -30,59 +31,68 @@ 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());
}

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());
}

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());
}

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());
}

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());
}

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());
}

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
Expand Down Expand Up @@ -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);
}
}