From fe87f3c056c6c9819168615029ab7506e3475f68 Mon Sep 17 00:00:00 2001 From: Marc Bennewitz Date: Thu, 15 Oct 2020 12:51:36 +0200 Subject: [PATCH] fixed return type detection of [self|static]::getValues() --- src/EnumDynamicReturnTypeExtension.php | 21 +++++++--- .../data/EnumGetValuesReturnType-3.json | 14 ++++++- .../data/EnumGetValuesReturnType-7.json | 12 ++++++ .../data/EnumGetValuesReturnType.php | 40 ++++++++++++++++++- 4 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 tests/integration/data/EnumGetValuesReturnType-7.json diff --git a/src/EnumDynamicReturnTypeExtension.php b/src/EnumDynamicReturnTypeExtension.php index 8944d41..a4de1fd 100644 --- a/src/EnumDynamicReturnTypeExtension.php +++ b/src/EnumDynamicReturnTypeExtension.php @@ -7,6 +7,7 @@ use PhpParser\Node\Expr\StaticCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; +use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\ConstantTypeHelper; @@ -81,10 +82,20 @@ public function getTypeFromStaticMethodCall( StaticCall $staticCall, Scope $scope ): Type { - $callClass = $staticCall->class->toString(); - $methodLower = strtolower($methodReflection->getName()); + $callClass = $staticCall->class->toString(); + + // Can't detect possible types on static::*() + // as it depends on defined enumerators of unknown inherited classes + if ($callClass === 'static') { + return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + } - return $this->staticMethods[$methodLower]($callClass); + if ($callClass === 'self') { + $callClass = $scope->getClassReflection()->getName(); + } + + $methodLower = strtolower($methodReflection->getName()); + return $this->objectMethods[$methodLower]($callClass); } public function getTypeFromMethodCall( @@ -92,11 +103,9 @@ public function getTypeFromMethodCall( MethodCall $methodCall, Scope $scope ): Type { - $callType = $scope->getType($methodCall->var); - $callClasses = $callType->getReferencedClasses(); $methodLower = strtolower($methodReflection->getName()); $returnTypes = []; - foreach ($callClasses as $callClass) { + foreach ($scope->getType($methodCall->var)->getReferencedClasses() as $callClass) { $returnTypes[] = $this->objectMethods[$methodLower]($callClass); } diff --git a/tests/integration/data/EnumGetValuesReturnType-3.json b/tests/integration/data/EnumGetValuesReturnType-3.json index 60aa65c..d813153 100644 --- a/tests/integration/data/EnumGetValuesReturnType-3.json +++ b/tests/integration/data/EnumGetValuesReturnType-3.json @@ -1,12 +1,22 @@ [ { - "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::staticMethodFail() should return array but returns array.", + "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::staticMethodFail() should return array but returns array.", "line": 24, "ignorable": true }, { - "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::objectMethodFail() should return array but returns array.", + "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::objectMethodFail() should return array but returns array.", "line": 36, "ignorable": true + }, + { + "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyEnum::selfGetValuesFail() should return array but returns array.", + "line": 54, + "ignorable": true + }, + { + "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyInheritedEnum::inheritSelfGetValuesFail() should return array but returns array.", + "line": 77, + "ignorable": true } ] \ No newline at end of file diff --git a/tests/integration/data/EnumGetValuesReturnType-7.json b/tests/integration/data/EnumGetValuesReturnType-7.json new file mode 100644 index 0000000..3ced2eb --- /dev/null +++ b/tests/integration/data/EnumGetValuesReturnType-7.json @@ -0,0 +1,12 @@ +[ + { + "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyEnum::staticGetValuesFail() should return array but returns array.", + "line": 60, + "ignorable": true + }, + { + "message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyInheritedEnum::inheritStaticGetValuesFail() should return array but returns array.", + "line": 83, + "ignorable": true + } +] \ No newline at end of file diff --git a/tests/integration/data/EnumGetValuesReturnType.php b/tests/integration/data/EnumGetValuesReturnType.php index 2f4c2e8..52aa347 100644 --- a/tests/integration/data/EnumGetValuesReturnType.php +++ b/tests/integration/data/EnumGetValuesReturnType.php @@ -18,7 +18,7 @@ public static function staticMethodValid(): array return MyInheritedEnum::getValues(); } - /** @return array */ + /** @return array */ public static function staticMethodFail(): array { return MyInheritedEnum::getValues(); @@ -30,7 +30,7 @@ public static function objectMethodValid(): array return MyInheritedEnum::STR()->getValues(); } - /** @return array */ + /** @return array */ public static function objectMethodFail(): array { return MyInheritedEnum::STR()->getValues(); @@ -41,9 +41,45 @@ class MyEnum extends Enum { const STR = 'str'; const INT = 1; + + /** @return array */ + public static function selfGetValuesValid(): array + { + return self::getValues(); + } + + /** @return array */ + public static function selfGetValuesFail(): array + { + return self::getValues(); + } + + /** @return array */ + public static function staticGetValuesFail(): array + { + return static::getValues(); + } } class MyInheritedEnum extends MyEnum { const FLOAT = 1.1; + + /** @return array */ + public static function inheritSelfGetValuesValid(): array + { + return self::getValues(); + } + + /** @return array */ + public static function inheritSelfGetValuesFail(): array + { + return self::getValues(); + } + + /** @return array */ + public static function inheritStaticGetValuesFail(): array + { + return static::getValues(); + } }