From fdc8a6da8830fd90b392e9a0a6832b8ead3fd81f Mon Sep 17 00:00:00 2001 From: Richard van Velzen Date: Tue, 6 Sep 2022 14:20:37 +0200 Subject: [PATCH 1/2] Implement template default syntax --- src/Ast/PhpDoc/TemplateTagValueNode.php | 9 +++++++-- src/Parser/PhpDocParser.php | 8 +++++++- tests/PHPStan/Parser/PhpDocParserTest.php | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Ast/PhpDoc/TemplateTagValueNode.php b/src/Ast/PhpDoc/TemplateTagValueNode.php index 1595956b..1d3c70e4 100644 --- a/src/Ast/PhpDoc/TemplateTagValueNode.php +++ b/src/Ast/PhpDoc/TemplateTagValueNode.php @@ -17,13 +17,17 @@ class TemplateTagValueNode implements PhpDocTagValueNode /** @var TypeNode|null */ public $bound; + /** @var TypeNode|null */ + public $default; + /** @var string (may be empty) */ public $description; - public function __construct(string $name, ?TypeNode $bound, string $description) + public function __construct(string $name, ?TypeNode $bound, string $description, ?TypeNode $default = null) { $this->name = $name; $this->bound = $bound; + $this->default = $default; $this->description = $description; } @@ -31,7 +35,8 @@ public function __construct(string $name, ?TypeNode $bound, string $description) public function __toString(): string { $bound = $this->bound !== null ? " of {$this->bound}" : ''; - return trim("{$this->name}{$bound} {$this->description}"); + $default = $this->default !== null ? " = {$this->default}" : ''; + return trim("{$this->name}{$bound}{$default} {$this->description}"); } } diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php index cca8e9bd..2b6b796d 100644 --- a/src/Parser/PhpDocParser.php +++ b/src/Parser/PhpDocParser.php @@ -402,9 +402,15 @@ private function parseTemplateTagValue(TokenIterator $tokens): Ast\PhpDoc\Templa $bound = null; } + if ($tokens->tryConsumeTokenValue('=')) { + $default = $this->typeParser->parse($tokens); + } else { + $default = null; + } + $description = $this->parseOptionalDescription($tokens); - return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description); + return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description, $default); } private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index 791e995c..217a71ea 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -3345,6 +3345,22 @@ public function provideTemplateTagsData(): Iterator ), ]), ]; + + yield [ + 'OK with default', + '/** @template T = string */', + new PhpDocNode([ + new PhpDocTagNode( + '@template', + new TemplateTagValueNode( + 'T', + null, + '', + new IdentifierTypeNode('string') + ) + ), + ]), + ]; } public function provideExtendsTagsData(): Iterator From f3e2a98af17cfe2a6ae022e7bd23f386c44a3d5a Mon Sep 17 00:00:00 2001 From: Richard van Velzen Date: Wed, 7 Sep 2022 10:20:07 +0200 Subject: [PATCH 2/2] Add tests for template bound + default --- tests/PHPStan/Parser/PhpDocParserTest.php | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index 217a71ea..69186a4b 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -3361,6 +3361,38 @@ public function provideTemplateTagsData(): Iterator ), ]), ]; + + yield [ + 'OK with default and description', + '/** @template T = string the value type */', + new PhpDocNode([ + new PhpDocTagNode( + '@template', + new TemplateTagValueNode( + 'T', + null, + 'the value type', + new IdentifierTypeNode('string') + ) + ), + ]), + ]; + + yield [ + 'OK with bound and default and description', + '/** @template T of string = \'\' the value type */', + new PhpDocNode([ + new PhpDocTagNode( + '@template', + new TemplateTagValueNode( + 'T', + new IdentifierTypeNode('string'), + 'the value type', + new ConstTypeNode(new ConstExprStringNode('')) + ) + ), + ]), + ]; } public function provideExtendsTagsData(): Iterator