From ce24060e9baed9636378589bbc9c71e2379ed535 Mon Sep 17 00:00:00 2001 From: Daniel Cannon <56539097+daniel-cannon@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:51:54 +0000 Subject: [PATCH 1/7] Fix class declaration detection after ::class references --- src/Commands/BaseCommand.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Commands/BaseCommand.php b/src/Commands/BaseCommand.php index 0423511..2eaa239 100644 --- a/src/Commands/BaseCommand.php +++ b/src/Commands/BaseCommand.php @@ -182,16 +182,14 @@ protected function findClassTokenIndex(array $tokens): ?int for ($lookahead = $index + 1; isset($tokens[$lookahead]); $lookahead++) { $nextToken = $tokens[$lookahead]; - if (!is_array($nextToken)) { - continue; - } - - if (in_array($nextToken[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) { - continue; - } + if (is_array($nextToken)) { + if (in_array($nextToken[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) { + continue; + } - if ($nextToken[0] === T_STRING) { - return $index; + if ($nextToken[0] === T_STRING) { + return $index; + } } break; From 8e1cf652f421cc7f6440a2f9cc4c73049fb14351 Mon Sep 17 00:00:00 2001 From: Daniel Cannon <56539097+daniel-cannon@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:52:02 +0000 Subject: [PATCH 2/7] Add regression test for ::class followed by enum case --- .../GenerateInterfaceUnionsCommandTest.php | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/Commands/GenerateInterfaceUnionsCommandTest.php b/tests/Commands/GenerateInterfaceUnionsCommandTest.php index 9bf3ee2..bce1c7b 100644 --- a/tests/Commands/GenerateInterfaceUnionsCommandTest.php +++ b/tests/Commands/GenerateInterfaceUnionsCommandTest.php @@ -21,11 +21,11 @@ public function testCommandExports(): void $output = file_get_contents('./models/index.d.ts'); $this->assertIsString($output); - $this->assertStringContainsString('"App\\\\Models\\\\AliasAnimal"', $output); - $this->assertStringContainsString('"App\\\\Models\\\\AttributeClassReferenceAnimal"', $output); - $this->assertStringContainsString('"App\\\\Models\\\\FullyQualifiedAnimal"', $output); - $this->assertStringContainsString('"App\\\\Models\\\\GroupedUseAnimal"', $output); - $this->assertStringContainsString('"App\\\\Models\\\\InheritedAnimal"', $output); + $this->assertStringContainsString('"App\\Models\\AliasAnimal"', $output); + $this->assertStringContainsString('"App\\Models\\AttributeClassReferenceAnimal"', $output); + $this->assertStringContainsString('"App\\Models\\FullyQualifiedAnimal"', $output); + $this->assertStringContainsString('"App\\Models\\GroupedUseAnimal"', $output); + $this->assertStringContainsString('"App\\Models\\InheritedAnimal"', $output); } public function testCommandExportsFullClassNameWhenDeclarationCrossesChunkBoundary(): void @@ -53,7 +53,7 @@ public function testCommandExportsFullClassNameWhenDeclarationCrossesChunkBounda $this->assertIsString($output); $this->assertStringContainsString( - '"App\\\\Models\\\\SomeLongClassNameThatCrossesTheLegacyChunkBoundary"', + '"App\\Models\\SomeLongClassNameThatCrossesTheLegacyChunkBoundary"', $output ); } @@ -121,6 +121,17 @@ public function testParserIgnoresClassConstantReferencesBeforeClassDeclaration() $this->assertSame(['App\\Interfaces\\AnimalInterface'], $info['implements']); } + public function testParserIgnoresClassConstantReferencesBeforeClassDeclarationWhenFollowedByEnumCase(): void + { + $info = $this->makeParserCommand()->classInfo( + './workbench/app/Models/AttributeClassAndEnumReferenceAnimal.php' + ); + + $this->assertSame('AttributeClassAndEnumReferenceAnimal', $info['class']); + $this->assertSame('App\\Models\\AttributeClassAndEnumReferenceAnimal', $info['fqcn']); + $this->assertSame(['App\\Interfaces\\AnimalInterface'], $info['implements']); + } + public function testCommandFailsForInvalidInputPath(): void { $this->expectException(\RuntimeException::class); @@ -136,4 +147,4 @@ protected function makeParserCommand(): TestableGenerateInterfaceUnionsCommand { return new TestableGenerateInterfaceUnionsCommand(new Filesystem()); } -} +} \ No newline at end of file From 80156d43dbf0b7e53421db97694a544f9573b82a Mon Sep 17 00:00:00 2001 From: Daniel Cannon <56539097+daniel-cannon@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:52:09 +0000 Subject: [PATCH 3/7] Add workbench fixture for class constant and enum attribute parsing --- .../Models/AttributeClassAndEnumReferenceAnimal.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 workbench/app/Models/AttributeClassAndEnumReferenceAnimal.php diff --git a/workbench/app/Models/AttributeClassAndEnumReferenceAnimal.php b/workbench/app/Models/AttributeClassAndEnumReferenceAnimal.php new file mode 100644 index 0000000..8e2eb4b --- /dev/null +++ b/workbench/app/Models/AttributeClassAndEnumReferenceAnimal.php @@ -0,0 +1,12 @@ + Date: Mon, 23 Mar 2026 16:57:24 +0000 Subject: [PATCH 4/7] Add trailing newline to regression test file --- tests/Commands/GenerateInterfaceUnionsCommandTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Commands/GenerateInterfaceUnionsCommandTest.php b/tests/Commands/GenerateInterfaceUnionsCommandTest.php index bce1c7b..25f827e 100644 --- a/tests/Commands/GenerateInterfaceUnionsCommandTest.php +++ b/tests/Commands/GenerateInterfaceUnionsCommandTest.php @@ -147,4 +147,4 @@ protected function makeParserCommand(): TestableGenerateInterfaceUnionsCommand { return new TestableGenerateInterfaceUnionsCommand(new Filesystem()); } -} \ No newline at end of file +} From 42eaa5575c546f195d14dc84f6b4a5cd5d1e1dbc Mon Sep 17 00:00:00 2001 From: Daniel Cannon <56539097+daniel-cannon@users.noreply.github.com> Date: Mon, 23 Mar 2026 17:03:18 +0000 Subject: [PATCH 5/7] Fix escaped backslash expectations in interface union tests From b963ff34877f9f5884611ac0521e5a60c2c0473c Mon Sep 17 00:00:00 2001 From: Daniel Cannon <56539097+daniel-cannon@users.noreply.github.com> Date: Mon, 23 Mar 2026 17:07:33 +0000 Subject: [PATCH 6/7] Correct expected escaping in interface union assertions From 7eb9e0c51d36520310d763c8dc13804ed12b72c0 Mon Sep 17 00:00:00 2001 From: Daniel Cannon Date: Mon, 23 Mar 2026 17:12:14 +0000 Subject: [PATCH 7/7] Fix interface union test escaping --- .../Commands/GenerateInterfaceUnionsCommandTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/Commands/GenerateInterfaceUnionsCommandTest.php b/tests/Commands/GenerateInterfaceUnionsCommandTest.php index 25f827e..4d634ce 100644 --- a/tests/Commands/GenerateInterfaceUnionsCommandTest.php +++ b/tests/Commands/GenerateInterfaceUnionsCommandTest.php @@ -21,11 +21,11 @@ public function testCommandExports(): void $output = file_get_contents('./models/index.d.ts'); $this->assertIsString($output); - $this->assertStringContainsString('"App\\Models\\AliasAnimal"', $output); - $this->assertStringContainsString('"App\\Models\\AttributeClassReferenceAnimal"', $output); - $this->assertStringContainsString('"App\\Models\\FullyQualifiedAnimal"', $output); - $this->assertStringContainsString('"App\\Models\\GroupedUseAnimal"', $output); - $this->assertStringContainsString('"App\\Models\\InheritedAnimal"', $output); + $this->assertStringContainsString('"App\\\\Models\\\\AliasAnimal"', $output); + $this->assertStringContainsString('"App\\\\Models\\\\AttributeClassReferenceAnimal"', $output); + $this->assertStringContainsString('"App\\\\Models\\\\FullyQualifiedAnimal"', $output); + $this->assertStringContainsString('"App\\\\Models\\\\GroupedUseAnimal"', $output); + $this->assertStringContainsString('"App\\\\Models\\\\InheritedAnimal"', $output); } public function testCommandExportsFullClassNameWhenDeclarationCrossesChunkBoundary(): void @@ -53,7 +53,7 @@ public function testCommandExportsFullClassNameWhenDeclarationCrossesChunkBounda $this->assertIsString($output); $this->assertStringContainsString( - '"App\\Models\\SomeLongClassNameThatCrossesTheLegacyChunkBoundary"', + '"App\\\\Models\\\\SomeLongClassNameThatCrossesTheLegacyChunkBoundary"', $output ); }