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.
This commit is contained in:
Jaapio
2024-04-08 21:38:01 +02:00
parent f359e4f8ef
commit edd5f67143
4 changed files with 51 additions and 1 deletions
@@ -7,6 +7,7 @@ namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
use Doctrine\Deprecations\Deprecation; use Doctrine\Deprecations\Deprecation;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\DocBlock\Tags\Param; use phpDocumentor\Reflection\DocBlock\Tags\Param;
use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context; use phpDocumentor\Reflection\Types\Context;
@@ -15,6 +16,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TypelessParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\TypelessParamTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
use function sprintf; use function sprintf;
@@ -59,6 +61,13 @@ final class ParamFactory implements PHPStanFactory
] ]
); );
if (($tagValue->type ?? null) instanceof OffsetAccessTypeNode) {
return InvalidTag::create(
(string) $tagValue,
'param'
);
}
return new Param( return new Param(
trim($tagValue->parameterName, '$'), trim($tagValue->parameterName, '$'),
$this->typeResolver->createType($tagValue->type ?? new IdentifierTypeNode('mixed'), $context), $this->typeResolver->createType($tagValue->type ?? new IdentifierTypeNode('mixed'), $context),
+8
View File
@@ -13,9 +13,11 @@ declare(strict_types=1);
namespace phpDocumentor\Reflection\DocBlock\Tags; namespace phpDocumentor\Reflection\DocBlock\Tags;
use InvalidArgumentException;
use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\Type;
use function in_array; use function in_array;
use function sprintf;
use function strlen; use function strlen;
use function substr; use function substr;
use function trim; use function trim;
@@ -59,6 +61,12 @@ abstract class TagWithType extends BaseTag
} }
} }
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))); $description = trim(substr($body, strlen($type)));
return [$type, $description]; return [$type, $description];
@@ -17,6 +17,7 @@ use Mockery as m;
use phpDocumentor\Reflection\DocBlock\Description; use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\StandardTagFactory; use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\DocBlock\Tags\Method; use phpDocumentor\Reflection\DocBlock\Tags\Method;
use phpDocumentor\Reflection\DocBlock\Tags\MethodParameter; use phpDocumentor\Reflection\DocBlock\Tags\MethodParameter;
use phpDocumentor\Reflection\DocBlock\Tags\Param; use phpDocumentor\Reflection\DocBlock\Tags\Param;
@@ -218,4 +219,30 @@ DOC;
$docblock->getTags() $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()
);
}
} }
@@ -14,6 +14,8 @@ declare(strict_types=1);
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
use phpDocumentor\Reflection\DocBlock\Description; 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\DocBlock\Tags\Param;
use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\PseudoTypes\IntegerValue; use phpDocumentor\Reflection\PseudoTypes\IntegerValue;
@@ -33,7 +35,7 @@ final class ParamFactoryTest extends TagFactoryTestCase
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory::supports * @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory::supports
* @dataProvider paramInputProvider * @dataProvider paramInputProvider
*/ */
public function testParamIsCreated(string $input, Param $expected): void public function testParamIsCreated(string $input, Tag $expected): void
{ {
$ast = $this->parseTag($input); $ast = $this->parseTag($input);
$factory = new ParamFactory($this->giveTypeResolver(), $this->givenDescriptionFactory()); $factory = new ParamFactory($this->giveTypeResolver(), $this->givenDescriptionFactory());
@@ -122,6 +124,10 @@ final class ParamFactoryTest extends TagFactoryTestCase
false false
), ),
], ],
[
'@param array[\Illuminate\Notifications\Channels\Notification] $notification',
InvalidTag::create('array[\Illuminate\Notifications\Channels\Notification] $notification', 'param'),
],
]; ];
} }
} }