From de6eace14e8c6e10e7071bcc6d65832a67cdea52 Mon Sep 17 00:00:00 2001 From: Brad <28307684+mad-briller@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:26:06 +0000 Subject: [PATCH] Refactor duplicated template parsing code into TypeParser. --- src/Ast/Type/CallableTypeNode.php | 15 ++++--- src/Ast/Type/CallableTypeTemplateNode.php | 35 --------------- src/Parser/PhpDocParser.php | 41 +++++------------ src/Parser/TypeParser.php | 54 ++++++++++++++++------- src/Printer/Printer.php | 14 ++---- tests/PHPStan/Parser/TypeParserTest.php | 12 ++--- tests/PHPStan/Printer/PrinterTest.php | 8 ++-- 7 files changed, 72 insertions(+), 107 deletions(-) delete mode 100644 src/Ast/Type/CallableTypeTemplateNode.php diff --git a/src/Ast/Type/CallableTypeNode.php b/src/Ast/Type/CallableTypeNode.php index 87a484e9..4c913198 100644 --- a/src/Ast/Type/CallableTypeNode.php +++ b/src/Ast/Type/CallableTypeNode.php @@ -3,6 +3,7 @@ namespace PHPStan\PhpDocParser\Ast\Type; use PHPStan\PhpDocParser\Ast\NodeAttributes; +use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; use function implode; class CallableTypeNode implements TypeNode @@ -13,8 +14,8 @@ class CallableTypeNode implements TypeNode /** @var IdentifierTypeNode */ public $identifier; - /** @var CallableTypeTemplateNode[] */ - public $templates; + /** @var TemplateTagValueNode[] */ + public $templateTypes; /** @var CallableTypeParameterNode[] */ public $parameters; @@ -24,14 +25,14 @@ class CallableTypeNode implements TypeNode /** * @param CallableTypeParameterNode[] $parameters - * @param CallableTypeTemplateNode[] $templates + * @param TemplateTagValueNode[] $templateTypes */ - public function __construct(IdentifierTypeNode $identifier, array $parameters, TypeNode $returnType, array $templates = []) + public function __construct(IdentifierTypeNode $identifier, array $parameters, TypeNode $returnType, array $templateTypes = []) { $this->identifier = $identifier; $this->parameters = $parameters; $this->returnType = $returnType; - $this->templates = $templates; + $this->templateTypes = $templateTypes; } @@ -41,8 +42,8 @@ public function __toString(): string if ($returnType instanceof self) { $returnType = "({$returnType})"; } - $template = $this->templates !== [] - ? '<' . implode(', ', $this->templates) . '>' + $template = $this->templateTypes !== [] + ? '<' . implode(', ', $this->templateTypes) . '>' : ''; $parameters = implode(', ', $this->parameters); return "{$this->identifier}{$template}({$parameters}): {$returnType}"; diff --git a/src/Ast/Type/CallableTypeTemplateNode.php b/src/Ast/Type/CallableTypeTemplateNode.php deleted file mode 100644 index 5f1d6cce..00000000 --- a/src/Ast/Type/CallableTypeTemplateNode.php +++ /dev/null @@ -1,35 +0,0 @@ -identifier = $identifier; - $this->bound = $bound; - } - - public function __toString(): string - { - $res = (string) $this->identifier; - if ($this->bound !== null) { - $res .= ' of ' . $this->bound; - } - - return $res; - } - -} diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php index 15a2aa5c..0eb191c4 100644 --- a/src/Parser/PhpDocParser.php +++ b/src/Parser/PhpDocParser.php @@ -439,7 +439,12 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph case '@template-contravariant': case '@phpstan-template-contravariant': case '@psalm-template-contravariant': - $tagValue = $this->parseTemplateTagValue($tokens, true); + $tagValue = $this->typeParser->parseTemplateTagValue( + $tokens, + function ($tokens) { + return $this->parseOptionalDescription($tokens); + } + ); break; case '@extends': @@ -923,7 +928,12 @@ private function parseMethodTagValue(TokenIterator $tokens): Ast\PhpDoc\MethodTa do { $startLine = $tokens->currentTokenLine(); $startIndex = $tokens->currentTokenIndex(); - $templateTypes[] = $this->enrichWithAttributes($tokens, $this->parseTemplateTagValue($tokens, false), $startLine, $startIndex); + $templateTypes[] = $this->enrichWithAttributes( + $tokens, + $this->typeParser->parseTemplateTagValue($tokens), + $startLine, + $startIndex + ); } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); } @@ -979,33 +989,6 @@ private function parseMethodTagValueParameter(TokenIterator $tokens): Ast\PhpDoc ); } - private function parseTemplateTagValue(TokenIterator $tokens, bool $parseDescription): Ast\PhpDoc\TemplateTagValueNode - { - $name = $tokens->currentTokenValue(); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - - if ($tokens->tryConsumeTokenValue('of') || $tokens->tryConsumeTokenValue('as')) { - $bound = $this->typeParser->parse($tokens); - - } else { - $bound = null; - } - - if ($tokens->tryConsumeTokenValue('=')) { - $default = $this->typeParser->parse($tokens); - } else { - $default = null; - } - - if ($parseDescription) { - $description = $this->parseOptionalDescription($tokens); - } else { - $description = ''; - } - - return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description, $default); - } - private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode { $startLine = $tokens->currentTokenLine(); diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php index 91083582..90c491fe 100644 --- a/src/Parser/TypeParser.php +++ b/src/Parser/TypeParser.php @@ -4,6 +4,7 @@ use LogicException; use PHPStan\PhpDocParser\Ast; +use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; use PHPStan\PhpDocParser\Lexer\Lexer; use function in_array; use function str_replace; @@ -460,6 +461,40 @@ public function parseGenericTypeArgument(TokenIterator $tokens): array return [$type, $variance]; } + /** + * @throws ParserException + * @param ?callable(TokenIterator): string $parseDescription + */ + public function parseTemplateTagValue( + TokenIterator $tokens, + ?callable $parseDescription = null + ): TemplateTagValueNode + { + $name = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + + if ($tokens->tryConsumeTokenValue('of') || $tokens->tryConsumeTokenValue('as')) { + $bound = $this->parse($tokens); + + } else { + $bound = null; + } + + if ($tokens->tryConsumeTokenValue('=')) { + $default = $this->parse($tokens); + } else { + $default = null; + } + + if ($parseDescription !== null) { + $description = $parseDescription($tokens); + } else { + $description = ''; + } + + return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description, $default); + } + /** @phpstan-impure */ private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier, bool $hasTemplate): Ast\Type\TypeNode @@ -497,7 +532,7 @@ private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNod /** - * @return Ast\Type\CallableTypeTemplateNode[] + * @return Ast\PhpDoc\TemplateTagValueNode[] * * @phpstan-impure */ @@ -527,27 +562,14 @@ private function parseCallableTemplates(TokenIterator $tokens): array } - private function parseCallableTemplateArgument(TokenIterator $tokens): Ast\Type\CallableTypeTemplateNode + private function parseCallableTemplateArgument(TokenIterator $tokens): Ast\PhpDoc\TemplateTagValueNode { $startLine = $tokens->currentTokenLine(); $startIndex = $tokens->currentTokenIndex(); - $identifier = $this->enrichWithAttributes( - $tokens, - new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()), - $startLine, - $startIndex - ); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - - $bound = null; - if ($tokens->tryConsumeTokenValue('of')) { - $bound = $this->parse($tokens); - } - return $this->enrichWithAttributes( $tokens, - new Ast\Type\CallableTypeTemplateNode($identifier, $bound), + $this->parseTemplateTagValue($tokens), $startLine, $startIndex ); diff --git a/src/Printer/Printer.php b/src/Printer/Printer.php index 00220eac..0f03ea2e 100644 --- a/src/Printer/Printer.php +++ b/src/Printer/Printer.php @@ -41,7 +41,6 @@ use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode; -use PHPStan\PhpDocParser\Ast\Type\CallableTypeTemplateNode; use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode; use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode; use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode; @@ -98,7 +97,7 @@ final class Printer ArrayShapeNode::class . '->items' => ', ', ObjectShapeNode::class . '->items' => ', ', CallableTypeNode::class . '->parameters' => ', ', - CallableTypeNode::class . '->templates' => ', ', + CallableTypeNode::class . '->templateTypes' => ', ', GenericTypeNode::class . '->genericTypes' => ', ', ConstExprArrayNode::class . '->items' => ', ', MethodTagValueNode::class . '->parameters' => ', ', @@ -226,11 +225,6 @@ function (PhpDocChildNode $child): string { $isOptional = $node->isOptional ? '=' : ''; return trim("{$type}{$isReference}{$isVariadic}{$node->parameterName}") . $isOptional; } - if ($node instanceof CallableTypeTemplateNode) { - $identifier = $this->printType($node->identifier); - $bound = $node->bound !== null ? ' of ' . $this->printType($node->bound) : ''; - return "{$identifier}{$bound}"; - } if ($node instanceof DoctrineAnnotation) { return (string) $node; } @@ -377,10 +371,10 @@ private function printType(TypeNode $node): string } else { $returnType = $this->printType($node->returnType); } - $template = $node->templates !== [] - ? '<' . implode(', ', array_map(function (CallableTypeTemplateNode $templateNode): string { + $template = $node->templateTypes !== [] + ? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateNode): string { return $this->print($templateNode); - }, $node->templates)) . '>' + }, $node->templateTypes)) . '>' : ''; $parameters = implode(', ', array_map(function (CallableTypeParameterNode $parameterNode): string { return $this->print($parameterNode); diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php index 88658fa2..b21e19ab 100644 --- a/tests/PHPStan/Parser/TypeParserTest.php +++ b/tests/PHPStan/Parser/TypeParserTest.php @@ -10,12 +10,12 @@ use PHPStan\PhpDocParser\Ast\ConstExpr\QuoteAwareConstExprStringNode; use PHPStan\PhpDocParser\Ast\Node; use PHPStan\PhpDocParser\Ast\NodeTraverser; +use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode; use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode; -use PHPStan\PhpDocParser\Ast\Type\CallableTypeTemplateNode; use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode; use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode; use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode; @@ -913,7 +913,7 @@ public function provideParseData(): array ], new IdentifierTypeNode('C'), [ - new CallableTypeTemplateNode(new IdentifierTypeNode('A'), null), + new TemplateTagValueNode('A', null, ''), ] ), ], @@ -951,7 +951,7 @@ public function provideParseData(): array new IdentifierTypeNode('false'), ]), [ - new CallableTypeTemplateNode(new IdentifierTypeNode('T'), new IdentifierTypeNode('Model')), + new TemplateTagValueNode('T', new IdentifierTypeNode('Model'), ''), ] ), ], @@ -988,11 +988,11 @@ public function provideParseData(): array ), ]), [ - new CallableTypeTemplateNode(new IdentifierTypeNode('Tx'), new UnionTypeNode([ + new TemplateTagValueNode('Tx', new UnionTypeNode([ new IdentifierTypeNode('X'), new IdentifierTypeNode('Z'), - ])), - new CallableTypeTemplateNode(new IdentifierTypeNode('Ty'), new IdentifierTypeNode('Y')), + ]), ''), + new TemplateTagValueNode('Ty', new IdentifierTypeNode('Y'), ''), ] ), ], diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php index 5d6fabc0..89bd5a3a 100644 --- a/tests/PHPStan/Printer/PrinterTest.php +++ b/tests/PHPStan/Printer/PrinterTest.php @@ -28,7 +28,6 @@ use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode; -use PHPStan\PhpDocParser\Ast\Type\CallableTypeTemplateNode; use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; @@ -595,9 +594,10 @@ public function enterNode(Node $node) public function enterNode(Node $node) { if ($node instanceof CallableTypeNode) { - $node->templates[] = new CallableTypeTemplateNode( - new IdentifierTypeNode('T'), - new IdentifierTypeNode('int') + $node->templateTypes[] = new TemplateTagValueNode( + 'T', + new IdentifierTypeNode('int'), + '' ); }