Skip to content

Commit

Permalink
Merge pull request #364 from phpDocumentor/fix/catch-invalid-type-cre…
Browse files Browse the repository at this point in the history
…ation

Added test for regression on invalid types
  • Loading branch information
jaapio authored Apr 9, 2024
2 parents 08acd34 + 64e172d commit 298d2fe
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 5 deletions.
15 changes: 10 additions & 5 deletions src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use RuntimeException;

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

foreach ($this->factories as $factory) {
if ($factory->supports($ast, $context)) {
return $factory->create($ast, $context);
try {
foreach ($this->factories as $factory) {
if ($factory->supports($ast, $context)) {
return $factory->create($ast, $context);
}
}
} catch (RuntimeException $e) {
return InvalidTag::create((string) $ast->value, 'method')->withError($e);
}

return InvalidTag::create(
$ast->name,
(string) $ast->value
(string) $ast->value,
$ast->name
);
}
}
194 changes: 194 additions & 0 deletions tests/integration/TypedTagsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
<?php

declare(strict_types=1);

namespace integration;

use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\DocBlock\Tags\Method;
use phpDocumentor\Reflection\DocBlock\Tags\Param;
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\PseudoTypes\ArrayShape;
use phpDocumentor\Reflection\PseudoTypes\ArrayShapeItem;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\Callable_;
use phpDocumentor\Reflection\Types\CallableParameter;
use phpDocumentor\Reflection\Types\Collection;
use phpDocumentor\Reflection\Types\Integer;
use phpDocumentor\Reflection\Types\Nullable;
use phpDocumentor\Reflection\Types\Object_;
use phpDocumentor\Reflection\Types\String_;
use phpDocumentor\Reflection\Types\Void_;
use PHPUnit\Framework\TestCase;

/**
* @coversNothing
*/
final class TypedTagsTest extends TestCase
{
/** @dataProvider typeProvider */
public function testParamFormats(string $type, Type $expectedType): void
{
$docblock = <<<DOCBLOCK
/**
* @param $type \$param
*/
DOCBLOCK;

$factory = DocBlockFactory::createInstance();
$phpdoc = $factory->create($docblock);

$this->assertInstanceOf(Param::class, $phpdoc->getTags()[0]);
$this->assertEquals($expectedType, $phpdoc->getTags()[0]->getType());
}

/** @dataProvider typeProvider */
public function testVarFormats(string $type, Type $expectedType): void
{
$docblock = <<<DOCBLOCK
/**
* @var $type \$param
*/
DOCBLOCK;

$factory = DocBlockFactory::createInstance();
$phpdoc = $factory->create($docblock);

$this->assertInstanceOf(Var_::class, $phpdoc->getTags()[0]);
$this->assertEquals($expectedType, $phpdoc->getTags()[0]->getType());
}

/** @dataProvider typeProvider */
public function testMethodFormats(string $type, Type $expectedType): void
{
$docblock = <<<DOCBLOCK
/**
* @method $type setValue($type \$param)
*/
DOCBLOCK;

$factory = DocBlockFactory::createInstance();
$phpdoc = $factory->create($docblock);

$this->assertInstanceOf(Method::class, $phpdoc->getTags()[0]);
$this->assertEquals($expectedType, $phpdoc->getTags()[0]->getReturnType());
$this->assertEquals($expectedType, $phpdoc->getTags()[0]->getParameters()[0]->getType());
}

/** @dataProvider invalidFormatsProvider */
public function testInvalidParam(string $type): void
{
$docblock = <<<DOCBLOCK
/**
* @param $type \$param
*/
DOCBLOCK;

$factory = DocBlockFactory::createInstance();
$phpdoc = $factory->create($docblock);

$this->assertInstanceOf(InvalidTag::class, $phpdoc->getTags()[0]);
$this->assertEquals("$type \$param", (string) $phpdoc->getTags()[0]);

}

/** @dataProvider invalidFormatsProvider */
public function testInvalidMethod(string $type): void
{
$docblock = <<<DOCBLOCK
/**
* @method $type setValue($type \$param)
*/
DOCBLOCK;

$factory = DocBlockFactory::createInstance();
$phpdoc = $factory->create($docblock);

$this->assertInstanceOf(InvalidTag::class, $phpdoc->getTags()[0]);
$this->assertEquals("$type setValue($type \$param)", (string) $phpdoc->getTags()[0]);

}

public static function typeProvider(): array
{
return [
[
'int',
new Integer(),
],
[
'integer',
new Integer(),
],
[
'string',
new String_(),
],
[
'T',
new Object_(new Fqsen('\\T')),
],
[
'array',
new Array_(),
],
[
'int[]',
new Array_(new Integer()),
],
[
'int[][]',
new Array_(new Array_(new Integer())),
],
[
'array<int, string>',
new Array_(new String_(), new Integer()),
],
[
'array{
foo: int,
bar?: ?string
}',
new ArrayShape(
new ArrayShapeItem('foo', new Integer(), false),
new ArrayShapeItem('bar', new Nullable(new String_()), true),

)
],
[
'Collection<int, string>',
new Collection(new Fqsen('\\Collection'), new String_(), new Integer()),
],
[
'Collection<TKey, Tvalue>',
new Collection(new Fqsen('\\Collection'), new Object_(new Fqsen('\\Tvalue')), new Object_(new Fqsen('\\TKey'))),
],
[
'Collection<Tvalue>',
new Collection(new Fqsen('\\Collection'), new Object_(new Fqsen('\\Tvalue'))),
],
[
'callable(int $foo, string $bar): void',
new Callable_([new CallableParameter(new Integer(), 'foo'), new CallableParameter(new String_(), 'bar')], new Void_()),
],
];
}

public static function invalidFormatsProvider(): \Generator
{
yield from [
'invalid collection' => ['self<Tvalue>'],
];

foreach (['<', '{', '('] as $badChar) {
yield "invalid format open $badChar" => ["array$badChar\\fooo"];
}

foreach (['>', '}', ')'] as $badChar) {
yield "invalid format close $badChar" => ["array\\fooo$badChar"];
}
}
}

0 comments on commit 298d2fe

Please sign in to comment.