From 423eaa4e223c9eef0dc6a35e6131afe5f5134fcf Mon Sep 17 00:00:00 2001 From: patrickkusebauch Date: Fri, 30 Aug 2024 19:29:22 +0200 Subject: [PATCH 1/3] Fix `get_debug_type` produces wrong type for anonymous classes with parent Fix poorly documented case of `get_debug_type` where it provides more specific output in cases where the anonymous class extends another class or implements an interface. --- .../Php/GetDebugTypeFunctionReturnTypeExtension.php | 11 ++++++++++- tests/PHPStan/Analyser/nsrt/get-debug-type.php | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php b/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php index 936f5b5adf..72990a6448 100644 --- a/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php +++ b/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php @@ -37,6 +37,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, /** * @see https://www.php.net/manual/en/function.get-debug-type.php#refsect1-function.get-debug-type-returnvalues + * @see https://github.com/php/php-src/commit/ef0e4478c51540510b67f7781ad240f5e0592ee4 */ private static function resolveOneType(Type $type): Type { @@ -71,7 +72,15 @@ private static function resolveOneType(Type $type): Type } if ($reflection->isAnonymous()) { // phpcs:ignore - $types[] = new ConstantStringType('class@anonymous'); + $parentClass = $reflection->getParentClass(); + $implementedInterfaces = $reflection->getImmediateInterfaces(); + if ($parentClass !== null) { + $types[] = $parentClass->getName() . '@anonymous'; + } elseif ($implementedInterfaces !== []) { + $types[] = $implementedInterfaces[0]->getName() . '@anonymous'; + } else { + $types[] = new ConstantStringType('class@anonymous'); + } } } diff --git a/tests/PHPStan/Analyser/nsrt/get-debug-type.php b/tests/PHPStan/Analyser/nsrt/get-debug-type.php index 975ea62613..322c33547f 100644 --- a/tests/PHPStan/Analyser/nsrt/get-debug-type.php +++ b/tests/PHPStan/Analyser/nsrt/get-debug-type.php @@ -5,6 +5,9 @@ use function PHPStan\Testing\assertType; final class A {} +interface B {} +interface C {} +class D {} /** * @param double $d @@ -18,6 +21,9 @@ function doFoo(bool $b, int $i, float $f, $d, $r, string $s, array $a, $intOrStr $o = new \stdClass(); $A = new A(); $anonymous = new class {}; + $anonymousImplements = new class implements B, C {}; + $anonymousExtends = new class extends D {}; + $anonymousExtendsImplements = new class extends D implements B, C {}; assertType("'bool'", get_debug_type($b)); assertType("'bool'", get_debug_type(true)); @@ -35,6 +41,9 @@ function doFoo(bool $b, int $i, float $f, $d, $r, string $s, array $a, $intOrStr assertType("'int'|'string'", get_debug_type($intOrString)); assertType("'array'|'GetDebugType\\\\A'", get_debug_type($arrayOrObject)); assertType("'class@anonymous'", get_debug_type($anonymous)); + assertType("'GetDebugType\\\\B@anonymous'", get_debug_type($anonymousImplements)); + assertType("'GetDebugType\\\\D@anonymous'", get_debug_type($anonymousExtends)); + assertType("'GetDebugType\\\\D@anonymous'", get_debug_type($anonymousExtendsImplements)); } /** From d40d5e5a83f0ab6cfa8267591ade60ea2a25fea1 Mon Sep 17 00:00:00 2001 From: patrickkusebauch Date: Fri, 30 Aug 2024 19:58:47 +0200 Subject: [PATCH 2/3] Fix return type and array access. --- src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php b/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php index 72990a6448..5e2a260f71 100644 --- a/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php +++ b/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php @@ -75,9 +75,10 @@ private static function resolveOneType(Type $type): Type $parentClass = $reflection->getParentClass(); $implementedInterfaces = $reflection->getImmediateInterfaces(); if ($parentClass !== null) { - $types[] = $parentClass->getName() . '@anonymous'; + $types[] = new ConstantStringType($parentClass->getName() . '@anonymous'); } elseif ($implementedInterfaces !== []) { - $types[] = $implementedInterfaces[0]->getName() . '@anonymous'; + $firstInterface = $implementedInterfaces[array_key_first($implementedInterfaces)]; + $types[] = new ConstantStringType($firstInterface->getName() . '@anonymous'); } else { $types[] = new ConstantStringType('class@anonymous'); } From 952dae2d04032adbd288f401078af18288aa0971 Mon Sep 17 00:00:00 2001 From: patrickkusebauch Date: Fri, 30 Aug 2024 20:08:03 +0200 Subject: [PATCH 3/3] Code style. --- src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php b/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php index 5e2a260f71..692c4968ad 100644 --- a/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php +++ b/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php @@ -11,6 +11,7 @@ use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; +use function array_key_first; use function array_map; use function count;