Skip to content

Commit 298d2fe

Browse files
authored
Merge pull request #364 from phpDocumentor/fix/catch-invalid-type-creation
Added test for regression on invalid types
2 parents 08acd34 + 64e172d commit 298d2fe

File tree

2 files changed

+204
-5
lines changed

2 files changed

+204
-5
lines changed

src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php

+10-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use PHPStan\PhpDocParser\Parser\PhpDocParser;
2222
use PHPStan\PhpDocParser\Parser\TokenIterator;
2323
use PHPStan\PhpDocParser\Parser\TypeParser;
24+
use RuntimeException;
2425

2526
/**
2627
* Factory class creating tags using phpstan's parser
@@ -54,15 +55,19 @@ public function create(string $tagLine, ?TypeContext $context = null): Tag
5455
$context = new TypeContext('');
5556
}
5657

57-
foreach ($this->factories as $factory) {
58-
if ($factory->supports($ast, $context)) {
59-
return $factory->create($ast, $context);
58+
try {
59+
foreach ($this->factories as $factory) {
60+
if ($factory->supports($ast, $context)) {
61+
return $factory->create($ast, $context);
62+
}
6063
}
64+
} catch (RuntimeException $e) {
65+
return InvalidTag::create((string) $ast->value, 'method')->withError($e);
6166
}
6267

6368
return InvalidTag::create(
64-
$ast->name,
65-
(string) $ast->value
69+
(string) $ast->value,
70+
$ast->name
6671
);
6772
}
6873
}

tests/integration/TypedTagsTest.php

+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace integration;
6+
7+
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
8+
use phpDocumentor\Reflection\DocBlock\Tags\Method;
9+
use phpDocumentor\Reflection\DocBlock\Tags\Param;
10+
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
11+
use phpDocumentor\Reflection\DocBlockFactory;
12+
use phpDocumentor\Reflection\Fqsen;
13+
use phpDocumentor\Reflection\PseudoTypes\ArrayShape;
14+
use phpDocumentor\Reflection\PseudoTypes\ArrayShapeItem;
15+
use phpDocumentor\Reflection\Type;
16+
use phpDocumentor\Reflection\Types\Array_;
17+
use phpDocumentor\Reflection\Types\Callable_;
18+
use phpDocumentor\Reflection\Types\CallableParameter;
19+
use phpDocumentor\Reflection\Types\Collection;
20+
use phpDocumentor\Reflection\Types\Integer;
21+
use phpDocumentor\Reflection\Types\Nullable;
22+
use phpDocumentor\Reflection\Types\Object_;
23+
use phpDocumentor\Reflection\Types\String_;
24+
use phpDocumentor\Reflection\Types\Void_;
25+
use PHPUnit\Framework\TestCase;
26+
27+
/**
28+
* @coversNothing
29+
*/
30+
final class TypedTagsTest extends TestCase
31+
{
32+
/** @dataProvider typeProvider */
33+
public function testParamFormats(string $type, Type $expectedType): void
34+
{
35+
$docblock = <<<DOCBLOCK
36+
/**
37+
* @param $type \$param
38+
*/
39+
DOCBLOCK;
40+
41+
$factory = DocBlockFactory::createInstance();
42+
$phpdoc = $factory->create($docblock);
43+
44+
$this->assertInstanceOf(Param::class, $phpdoc->getTags()[0]);
45+
$this->assertEquals($expectedType, $phpdoc->getTags()[0]->getType());
46+
}
47+
48+
/** @dataProvider typeProvider */
49+
public function testVarFormats(string $type, Type $expectedType): void
50+
{
51+
$docblock = <<<DOCBLOCK
52+
/**
53+
* @var $type \$param
54+
*/
55+
DOCBLOCK;
56+
57+
$factory = DocBlockFactory::createInstance();
58+
$phpdoc = $factory->create($docblock);
59+
60+
$this->assertInstanceOf(Var_::class, $phpdoc->getTags()[0]);
61+
$this->assertEquals($expectedType, $phpdoc->getTags()[0]->getType());
62+
}
63+
64+
/** @dataProvider typeProvider */
65+
public function testMethodFormats(string $type, Type $expectedType): void
66+
{
67+
$docblock = <<<DOCBLOCK
68+
/**
69+
* @method $type setValue($type \$param)
70+
*/
71+
DOCBLOCK;
72+
73+
$factory = DocBlockFactory::createInstance();
74+
$phpdoc = $factory->create($docblock);
75+
76+
$this->assertInstanceOf(Method::class, $phpdoc->getTags()[0]);
77+
$this->assertEquals($expectedType, $phpdoc->getTags()[0]->getReturnType());
78+
$this->assertEquals($expectedType, $phpdoc->getTags()[0]->getParameters()[0]->getType());
79+
}
80+
81+
/** @dataProvider invalidFormatsProvider */
82+
public function testInvalidParam(string $type): void
83+
{
84+
$docblock = <<<DOCBLOCK
85+
/**
86+
* @param $type \$param
87+
*/
88+
DOCBLOCK;
89+
90+
$factory = DocBlockFactory::createInstance();
91+
$phpdoc = $factory->create($docblock);
92+
93+
$this->assertInstanceOf(InvalidTag::class, $phpdoc->getTags()[0]);
94+
$this->assertEquals("$type \$param", (string) $phpdoc->getTags()[0]);
95+
96+
}
97+
98+
/** @dataProvider invalidFormatsProvider */
99+
public function testInvalidMethod(string $type): void
100+
{
101+
$docblock = <<<DOCBLOCK
102+
/**
103+
* @method $type setValue($type \$param)
104+
*/
105+
DOCBLOCK;
106+
107+
$factory = DocBlockFactory::createInstance();
108+
$phpdoc = $factory->create($docblock);
109+
110+
$this->assertInstanceOf(InvalidTag::class, $phpdoc->getTags()[0]);
111+
$this->assertEquals("$type setValue($type \$param)", (string) $phpdoc->getTags()[0]);
112+
113+
}
114+
115+
public static function typeProvider(): array
116+
{
117+
return [
118+
[
119+
'int',
120+
new Integer(),
121+
],
122+
[
123+
'integer',
124+
new Integer(),
125+
],
126+
[
127+
'string',
128+
new String_(),
129+
],
130+
[
131+
'T',
132+
new Object_(new Fqsen('\\T')),
133+
],
134+
[
135+
'array',
136+
new Array_(),
137+
],
138+
[
139+
'int[]',
140+
new Array_(new Integer()),
141+
],
142+
[
143+
'int[][]',
144+
new Array_(new Array_(new Integer())),
145+
],
146+
[
147+
'array<int, string>',
148+
new Array_(new String_(), new Integer()),
149+
],
150+
[
151+
'array{
152+
foo: int,
153+
bar?: ?string
154+
}',
155+
new ArrayShape(
156+
new ArrayShapeItem('foo', new Integer(), false),
157+
new ArrayShapeItem('bar', new Nullable(new String_()), true),
158+
159+
)
160+
],
161+
[
162+
'Collection<int, string>',
163+
new Collection(new Fqsen('\\Collection'), new String_(), new Integer()),
164+
],
165+
[
166+
'Collection<TKey, Tvalue>',
167+
new Collection(new Fqsen('\\Collection'), new Object_(new Fqsen('\\Tvalue')), new Object_(new Fqsen('\\TKey'))),
168+
],
169+
[
170+
'Collection<Tvalue>',
171+
new Collection(new Fqsen('\\Collection'), new Object_(new Fqsen('\\Tvalue'))),
172+
],
173+
[
174+
'callable(int $foo, string $bar): void',
175+
new Callable_([new CallableParameter(new Integer(), 'foo'), new CallableParameter(new String_(), 'bar')], new Void_()),
176+
],
177+
];
178+
}
179+
180+
public static function invalidFormatsProvider(): \Generator
181+
{
182+
yield from [
183+
'invalid collection' => ['self<Tvalue>'],
184+
];
185+
186+
foreach (['<', '{', '('] as $badChar) {
187+
yield "invalid format open $badChar" => ["array$badChar\\fooo"];
188+
}
189+
190+
foreach (['>', '}', ')'] as $badChar) {
191+
yield "invalid format close $badChar" => ["array\\fooo$badChar"];
192+
}
193+
}
194+
}

0 commit comments

Comments
 (0)