Skip to content

Commit 556c3f1

Browse files
Ignore non class types extracted from docblock definitions (#510)
1 parent fbc5e97 commit 556c3f1

File tree

3 files changed

+55
-13
lines changed

3 files changed

+55
-13
lines changed

src/Analyzer/DocblockTypesResolver.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ private function resolvePropertyTypes(Node $node): void
8282
$arrayItemType = $docblock->getVarTagTypes();
8383
$arrayItemType = array_pop($arrayItemType);
8484

85-
if (null !== $arrayItemType) {
85+
if ($this->isTypeClass($arrayItemType)) {
8686
$node->type = $this->resolveName(new Name($arrayItemType), Stmt\Use_::TYPE_NORMAL);
8787

8888
return;
@@ -129,7 +129,8 @@ private function resolveFunctionTypes(Node $node): void
129129

130130
$type = $docblock->getParamTagTypesByName('$'.$param->var->name);
131131

132-
if (null === $type) {
132+
// we ignore any type which is not a class
133+
if (!$this->isTypeClass($type)) {
133134
continue;
134135
}
135136

@@ -141,7 +142,8 @@ private function resolveFunctionTypes(Node $node): void
141142
$type = $docblock->getReturnTagTypes();
142143
$type = array_pop($type);
143144

144-
if (null === $type) {
145+
// we ignore any type which is not a class
146+
if (!$this->isTypeClass($type)) {
145147
return;
146148
}
147149

@@ -216,4 +218,18 @@ private function isTypeArray($type): bool
216218
{
217219
return null !== $type && isset($type->name) && 'array' === $type->name;
218220
}
221+
222+
/**
223+
* @psalm-assert-if-true string $fqcn
224+
*/
225+
private function isTypeClass(?string $fqcn): bool
226+
{
227+
if (null === $fqcn) {
228+
return false;
229+
}
230+
231+
$validFqcn = '/^[a-zA-Z0-9_\x7f-\xff\\\\]*[a-zA-Z0-9_\x7f-\xff]$/';
232+
233+
return (bool) preg_match($validFqcn, $fqcn);
234+
}
219235
}

tests/Unit/Analyzer/DocblockParserTest.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public function test_it_should_exctract_types_from_param_tag(): void
2222
* @param array<User> $user
2323
* @param int $aValue
2424
* @param MyPlainDto $plainDto
25+
* @param array<int, int|string> $unionType
26+
* @param array<int, array<int, int|string>> $nestedUnionType
2527
*/
2628
PHP;
2729

@@ -34,6 +36,8 @@ public function test_it_should_exctract_types_from_param_tag(): void
3436

3537
self::assertEquals('int', $db->getParamTagTypesByName('$aValue'));
3638
self::assertEquals('MyPlainDto', $db->getParamTagTypesByName('$plainDto'));
39+
self::assertEquals('(int | string)', $db->getParamTagTypesByName('$unionType'));
40+
self::assertEquals('array<int, (int | string)>', $db->getParamTagTypesByName('$nestedUnionType'));
3741
}
3842

3943
public function test_it_should_extract_return_type_from_return_tag(): void
@@ -48,19 +52,21 @@ public function test_it_should_extract_return_type_from_return_tag(): void
4852
* @return array<User>
4953
* @return int
5054
* @return MyPlainDto
55+
* @return array<int, int|string>
5156
*/
5257
PHP;
5358

5459
$db = $parser->parse($code);
5560

5661
$returnTypes = $db->getReturnTagTypes();
57-
self::assertCount(6, $returnTypes);
62+
self::assertCount(7, $returnTypes);
5863
self::assertEquals('MyDto', $returnTypes[0]);
5964
self::assertEquals('MyOtherDto', $returnTypes[1]);
6065
self::assertEquals('ValueObject', $returnTypes[2]);
6166
self::assertEquals('User', $returnTypes[3]);
6267
self::assertEquals('int', $returnTypes[4]);
6368
self::assertEquals('MyPlainDto', $returnTypes[5]);
69+
self::assertEquals('(int | string)', $returnTypes[6]);
6470
}
6571

6672
public function test_it_should_extract_types_from_var_tag(): void
@@ -75,19 +81,21 @@ public function test_it_should_extract_types_from_var_tag(): void
7581
* @var array<User> $user
7682
* @var int $aValue
7783
* @var MyPlainDto $plainDto
84+
* @var array<int, int|string> $unionType
7885
*/
7986
PHP;
8087

8188
$db = $parser->parse($code);
8289

8390
$varTags = $db->getVarTagTypes();
84-
self::assertCount(6, $varTags);
91+
self::assertCount(7, $varTags);
8592
self::assertEquals('MyDto', $varTags[0]);
8693
self::assertEquals('MyOtherDto', $varTags[1]);
8794
self::assertEquals('ValueObject', $varTags[2]);
8895
self::assertEquals('User', $varTags[3]);
8996
self::assertEquals('int', $varTags[4]);
9097
self::assertEquals('MyPlainDto', $varTags[5]);
98+
self::assertEquals('(int | string)', $varTags[6]);
9199
}
92100

93101
public function test_it_should_extract_doctrine_like_annotations(): void

tests/Unit/Analyzer/DocblockTypesResolverTest.php

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
class DocblockTypesResolverTest extends TestCase
1717
{
18-
public function test_it_should_boh(): void
18+
public function test_it_should_collect_dependencies_defined_in_docblock(): void
1919
{
2020
$parser = new FileParser(
2121
new NodeTraverser(),
@@ -36,6 +36,13 @@ public function test_it_should_boh(): void
3636
3737
class MyClass
3838
{
39+
/** @var array<int, int|string> */
40+
public array $myArray;
41+
42+
/** @var array<int, User> */
43+
public array $users;
44+
45+
3946
/**
4047
* @param MyDto[] $dtoList
4148
* @param int $var2
@@ -52,19 +59,30 @@ public function __construct(string $var1, array $dtoList, $var2, array $voList)
5259
public function myMethod(array $users, array $products, MyOtherClass $other): void
5360
{
5461
}
62+
63+
/**
64+
*
65+
* @param array<int, int|string> $aParam
66+
* @param array<int, User> $users
67+
*
68+
* @return array<int, int|string>
69+
*/
70+
public function myMethod2(array $aParam, array $users): array
5571
}
5672
EOF;
5773

58-
$parser->parse($code, 'boh');
74+
$parser->parse($code, 'src/path/file.php');
5975

6076
$cd = $parser->getClassDescriptions()[0];
6177
$dep = $cd->getDependencies();
6278

63-
self::assertCount(5, $cd->getDependencies());
64-
self::assertEquals('Application\MyDto', $dep[0]->getFQCN()->toString());
65-
self::assertEquals('Domain\ValueObject', $dep[1]->getFQCN()->toString());
66-
self::assertEquals('Application\Model\User', $dep[2]->getFQCN()->toString());
67-
self::assertEquals('Application\Model\Product', $dep[3]->getFQCN()->toString());
68-
self::assertEquals('Domain\Foo\MyOtherClass', $dep[4]->getFQCN()->toString());
79+
self::assertCount(7, $cd->getDependencies());
80+
self::assertEquals('Application\Model\User', $dep[0]->getFQCN()->toString());
81+
self::assertEquals('Application\MyDto', $dep[1]->getFQCN()->toString());
82+
self::assertEquals('Domain\ValueObject', $dep[2]->getFQCN()->toString());
83+
self::assertEquals('Application\Model\User', $dep[3]->getFQCN()->toString());
84+
self::assertEquals('Application\Model\Product', $dep[4]->getFQCN()->toString());
85+
self::assertEquals('Domain\Foo\MyOtherClass', $dep[5]->getFQCN()->toString());
86+
self::assertEquals('Application\Model\User', $dep[6]->getFQCN()->toString());
6987
}
7088
}

0 commit comments

Comments
 (0)