From edd5f67143e35f285bee0c252c612d69cac4ba36 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Mon, 8 Apr 2024 21:38:01 +0200 Subject: [PATCH] Be more strict about type definitions on param Throw on invalid type definitions and unexpected type definitions. Not all types resolved by phpstan's parser are valid for docblocks, they might in a more complex type system but I do not see how these types would ever apply to param tags. --- src/DocBlock/Tags/Factory/ParamFactory.php | 9 +++++++ src/DocBlock/Tags/TagWithType.php | 8 ++++++ .../integration/InterpretingDocBlocksTest.php | 27 +++++++++++++++++++ .../Tags/Factory/ParamFactoryTest.php | 8 +++++- 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/DocBlock/Tags/Factory/ParamFactory.php b/src/DocBlock/Tags/Factory/ParamFactory.php index c44fe792..8e9bcaac 100644 --- a/src/DocBlock/Tags/Factory/ParamFactory.php +++ b/src/DocBlock/Tags/Factory/ParamFactory.php @@ -7,6 +7,7 @@ use Doctrine\Deprecations\Deprecation; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; use phpDocumentor\Reflection\DocBlock\Tags\Param; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context; @@ -15,6 +16,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\PhpDocParser\Ast\PhpDoc\TypelessParamTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode; use Webmozart\Assert\Assert; use function sprintf; @@ -59,6 +61,13 @@ public function create(PhpDocTagNode $node, Context $context): Tag ] ); + if (($tagValue->type ?? null) instanceof OffsetAccessTypeNode) { + return InvalidTag::create( + (string) $tagValue, + 'param' + ); + } + return new Param( trim($tagValue->parameterName, '$'), $this->typeResolver->createType($tagValue->type ?? new IdentifierTypeNode('mixed'), $context), diff --git a/src/DocBlock/Tags/TagWithType.php b/src/DocBlock/Tags/TagWithType.php index 60067b2a..271c41b5 100644 --- a/src/DocBlock/Tags/TagWithType.php +++ b/src/DocBlock/Tags/TagWithType.php @@ -13,9 +13,11 @@ namespace phpDocumentor\Reflection\DocBlock\Tags; +use InvalidArgumentException; use phpDocumentor\Reflection\Type; use function in_array; +use function sprintf; use function strlen; use function substr; use function trim; @@ -59,6 +61,12 @@ protected static function extractTypeFromBody(string $body): array } } + if ($nestingLevel < 0 || $nestingLevel > 0) { + throw new InvalidArgumentException( + sprintf('Could not find type in %s, please check for malformed notations', $body) + ); + } + $description = trim(substr($body, strlen($type))); return [$type, $description]; diff --git a/tests/integration/InterpretingDocBlocksTest.php b/tests/integration/InterpretingDocBlocksTest.php index 586c64b6..1a4e7443 100644 --- a/tests/integration/InterpretingDocBlocksTest.php +++ b/tests/integration/InterpretingDocBlocksTest.php @@ -17,6 +17,7 @@ use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\StandardTagFactory; use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; use phpDocumentor\Reflection\DocBlock\Tags\Method; use phpDocumentor\Reflection\DocBlock\Tags\MethodParameter; use phpDocumentor\Reflection\DocBlock\Tags\Param; @@ -218,4 +219,30 @@ public function testMethodRegression(): void $docblock->getTags() ); } + + public function testInvalidTypeParamResultsInInvalidTag(): void + { + $docCommment = ' +/** + * This is an example of a summary. + * + * @param array\Foo> $test + */ +'; + $factory = DocBlockFactory::createInstance(); + $docblock = $factory->create($docCommment); + + self::assertEquals( + [ + InvalidTag::create( + 'array\Foo> $test', + 'param', + )->withError( + new \InvalidArgumentException( + 'Could not find type in array\Foo> $test, please check for malformed notations') + ), + ], + $docblock->getTags() + ); + } } diff --git a/tests/unit/DocBlock/Tags/Factory/ParamFactoryTest.php b/tests/unit/DocBlock/Tags/Factory/ParamFactoryTest.php index f284dec4..5c50181c 100644 --- a/tests/unit/DocBlock/Tags/Factory/ParamFactoryTest.php +++ b/tests/unit/DocBlock/Tags/Factory/ParamFactoryTest.php @@ -14,6 +14,8 @@ namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; use phpDocumentor\Reflection\DocBlock\Tags\Param; use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\PseudoTypes\IntegerValue; @@ -33,7 +35,7 @@ final class ParamFactoryTest extends TagFactoryTestCase * @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory::supports * @dataProvider paramInputProvider */ - public function testParamIsCreated(string $input, Param $expected): void + public function testParamIsCreated(string $input, Tag $expected): void { $ast = $this->parseTag($input); $factory = new ParamFactory($this->giveTypeResolver(), $this->givenDescriptionFactory()); @@ -122,6 +124,10 @@ public function paramInputProvider(): array false ), ], + [ + '@param array[\Illuminate\Notifications\Channels\Notification] $notification', + InvalidTag::create('array[\Illuminate\Notifications\Channels\Notification] $notification', 'param'), + ], ]; } }