From 8f2307e83c80a095af7554631da0c7876565a41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Thu, 19 Sep 2024 09:22:18 +0200 Subject: [PATCH] Display parent class name for anonymous class like native php does --- .../BetterReflectionProvider.php | 16 +++++- .../Analyser/AnalyserIntegrationTest.php | 4 +- .../PHPStan/Levels/data/stubValidator-0.json | 4 +- .../AnonymousClassReflectionTest.php | 49 +++++++++++++++++++ .../Reflection/data/anonymous-classes.php | 20 ++++++++ .../Classes/RequireImplementsRuleTest.php | 2 +- .../MissingMethodImplementationRuleTest.php | 2 +- .../MissingReadOnlyPropertyAssignRuleTest.php | 2 +- 8 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index 54b26ec9f8..be27dd03a0 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -51,6 +51,7 @@ use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Type; use function array_key_exists; +use function array_key_first; use function array_map; use function base64_decode; use function in_array; @@ -217,12 +218,23 @@ public function getAnonymousClassReflection(Node\Stmt\Class_ $classNode, Scope $ null, ); + $displayParentName = $reflectionClass->getParentClassName(); + if ($displayParentName === null) { + // https://3v4l.org/6FBuP + $classInterfaceNames = $reflectionClass->getInterfaceNames(); + if ($classInterfaceNames !== []) { + $displayParentName = $classInterfaceNames[array_key_first($classInterfaceNames)]; + } else { + $displayParentName = 'class'; + } + } + /** @var int|null $classLineIndex */ $classLineIndex = $classNode->getAttribute(AnonymousClassVisitor::ATTRIBUTE_LINE_INDEX); if ($classLineIndex === null) { - $displayName = sprintf('class@anonymous/%s:%s', $filename, $classNode->getStartLine()); + $displayName = sprintf('%s@anonymous/%s:%s', $displayParentName, $filename, $classNode->getStartLine()); } else { - $displayName = sprintf('class@anonymous/%s:%s:%d', $filename, $classNode->getStartLine(), $classLineIndex); + $displayName = sprintf('%s@anonymous/%s:%s:%d', $displayParentName, $filename, $classNode->getStartLine(), $classLineIndex); } self::$anonymousClasses[$className] = new ClassReflection( diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index 6fb9c0f411..207ff22e0b 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -541,9 +541,9 @@ public function testBug6442(): void { $errors = $this->runAnalyse(__DIR__ . '/data/bug-6442.php'); $this->assertCount(2, $errors); - $this->assertSame('Dumped type: \'Bug6442\\\A\'', $errors[0]->getMessage()); + $this->assertSame('Dumped type: \'Bug6442\\\B\'', $errors[0]->getMessage()); $this->assertSame(9, $errors[0]->getLine()); - $this->assertSame('Dumped type: \'Bug6442\\\B\'', $errors[1]->getMessage()); + $this->assertSame('Dumped type: \'Bug6442\\\A\'', $errors[1]->getMessage()); $this->assertSame(9, $errors[1]->getLine()); } diff --git a/tests/PHPStan/Levels/data/stubValidator-0.json b/tests/PHPStan/Levels/data/stubValidator-0.json index 0517d298af..ce13d6e997 100644 --- a/tests/PHPStan/Levels/data/stubValidator-0.json +++ b/tests/PHPStan/Levels/data/stubValidator-0.json @@ -20,12 +20,12 @@ "ignorable": false }, { - "message": "Method class@anonymous/stubValidator/stubs.php:27::doFoo() has no return type specified.", + "message": "Method ArrayIterator@anonymous/stubValidator/stubs.php:27::doFoo() has no return type specified.", "line": 30, "ignorable": false }, { - "message": "Parameter $foo of method class@anonymous/stubValidator/stubs.php:27::doFoo() has invalid type StubValidator\\Foooooooo.", + "message": "Parameter $foo of method ArrayIterator@anonymous/stubValidator/stubs.php:27::doFoo() has invalid type StubValidator\\Foooooooo.", "line": 30, "ignorable": false } diff --git a/tests/PHPStan/Reflection/AnonymousClassReflectionTest.php b/tests/PHPStan/Reflection/AnonymousClassReflectionTest.php index 9310f617f1..b6ee413032 100644 --- a/tests/PHPStan/Reflection/AnonymousClassReflectionTest.php +++ b/tests/PHPStan/Reflection/AnonymousClassReflectionTest.php @@ -101,6 +101,55 @@ public function testReflection(): void ]), 9, ], + [ + implode("\n", [ + 'name: AnonymousClass1d622e3ff3a656e68d55eafbd25eaef1', + 'display name: AnonymousClassReflectionTest\A@anonymous/tests/PHPStan/Reflection/data/anonymous-classes.php:17:1', + ]), + 17, + ], + [ + implode("\n", [ + 'name: AnonymousClass6e1acc8e948827c8d0439a2225fdbdd0', + 'display name: AnonymousClassReflectionTest\A@anonymous/tests/PHPStan/Reflection/data/anonymous-classes.php:17:2', + ]), + 17, + ], + [ + implode("\n", [ + 'name: AnonymousClass2a49db3d44479dddd8beaea4ea8131fb', + 'display name: AnonymousClassReflectionTest\A@anonymous/tests/PHPStan/Reflection/data/anonymous-classes.php:19:1', + ]), + 19, + ], + [ + implode("\n", [ + 'name: AnonymousClass337463cf86ee25e526f445630960b336', + 'display name: AnonymousClassReflectionTest\A@anonymous/tests/PHPStan/Reflection/data/anonymous-classes.php:19:2', + ]), + 19, + ], + [ + implode("\n", [ + 'name: AnonymousClassda3e79cc45f826d60295f848abab37e7', + 'display name: AnonymousClassReflectionTest\U@anonymous/tests/PHPStan/Reflection/data/anonymous-classes.php:29', + ]), + 29, + ], + [ + implode("\n", [ + 'name: AnonymousClassc06612bf3776bbe5e50870a8c3151186', + 'display name: AnonymousClassReflectionTest\U@anonymous/tests/PHPStan/Reflection/data/anonymous-classes.php:31', + ]), + 31, + ], + [ + implode("\n", [ + 'name: AnonymousClassbee6eba8c721d73d649fcc9d361f5902', + 'display name: AnonymousClassReflectionTest\V@anonymous/tests/PHPStan/Reflection/data/anonymous-classes.php:33', + ]), + 33, + ], ]); } diff --git a/tests/PHPStan/Reflection/data/anonymous-classes.php b/tests/PHPStan/Reflection/data/anonymous-classes.php index 9336316ff9..4024445aec 100644 --- a/tests/PHPStan/Reflection/data/anonymous-classes.php +++ b/tests/PHPStan/Reflection/data/anonymous-classes.php @@ -11,3 +11,23 @@ public function __construct(object $object) { } }; + +class A {} + +new class extends A {}; new class extends A {}; + +new class (new class extends A {}) extends A { + public function __construct(object $object) + { + } +}; + +interface U {} + +interface V {} + +new class implements U {}; + +new class implements U, V {}; + +new class implements V, U {}; diff --git a/tests/PHPStan/Rules/Classes/RequireImplementsRuleTest.php b/tests/PHPStan/Rules/Classes/RequireImplementsRuleTest.php index a2fe9cf7a3..6a147e9398 100644 --- a/tests/PHPStan/Rules/Classes/RequireImplementsRuleTest.php +++ b/tests/PHPStan/Rules/Classes/RequireImplementsRuleTest.php @@ -57,7 +57,7 @@ public function testRule(): void 137, ], [ - 'Trait IncompatibleRequireImplements\ValidPsalmTrait requires using class to implement IncompatibleRequireImplements\RequiredInterface2, but class@anonymous/tests/PHPStan/Rules/PhpDoc/data/incompatible-require-implements.php:164 does not.', + 'Trait IncompatibleRequireImplements\ValidPsalmTrait requires using class to implement IncompatibleRequireImplements\RequiredInterface2, but IncompatibleRequireImplements\RequiredInterface@anonymous/tests/PHPStan/Rules/PhpDoc/data/incompatible-require-implements.php:164 does not.', 164, ], [ diff --git a/tests/PHPStan/Rules/Methods/MissingMethodImplementationRuleTest.php b/tests/PHPStan/Rules/Methods/MissingMethodImplementationRuleTest.php index 82240f467e..babaadaae2 100644 --- a/tests/PHPStan/Rules/Methods/MissingMethodImplementationRuleTest.php +++ b/tests/PHPStan/Rules/Methods/MissingMethodImplementationRuleTest.php @@ -29,7 +29,7 @@ public function testRule(): void 24, ], [ - 'Non-abstract class class@anonymous/tests/PHPStan/Rules/Methods/data/missing-method-impl.php:41 contains abstract method doFoo() from interface MissingMethodImpl\Foo.', + 'Non-abstract class MissingMethodImpl\Foo@anonymous/tests/PHPStan/Rules/Methods/data/missing-method-impl.php:41 contains abstract method doFoo() from interface MissingMethodImpl\Foo.', 41, ], ]); diff --git a/tests/PHPStan/Rules/Properties/MissingReadOnlyPropertyAssignRuleTest.php b/tests/PHPStan/Rules/Properties/MissingReadOnlyPropertyAssignRuleTest.php index 02e81f3f55..3b481b8f14 100644 --- a/tests/PHPStan/Rules/Properties/MissingReadOnlyPropertyAssignRuleTest.php +++ b/tests/PHPStan/Rules/Properties/MissingReadOnlyPropertyAssignRuleTest.php @@ -307,7 +307,7 @@ public function testRedeclaredReadonlyProperties(): void 70, ], [ - 'Readonly property class@anonymous/tests/PHPStan/Rules/Properties/data/redeclare-readonly-property.php:117::$myProp is already assigned.', + 'Readonly property RedeclareReadonlyProperty\A@anonymous/tests/PHPStan/Rules/Properties/data/redeclare-readonly-property.php:117::$myProp is already assigned.', 121, ], [