From 441036c1624206be642d6cd7d07f834de3139416 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 18 Nov 2024 16:42:48 +0100 Subject: [PATCH] Support for `@pure-unless-parameter-passed` --- src/Ast/PhpDoc/PhpDocNode.php | 11 +++++++ ...ureUnlessParameterIsPassedTagValueNode.php | 29 +++++++++++++++++ src/Parser/PhpDocParser.php | 13 ++++++++ src/Printer/Printer.php | 4 +++ tests/PHPStan/Parser/PhpDocParserTest.php | 32 +++++++++++++++++++ tests/PHPStan/Printer/PrinterTest.php | 19 +++++++++++ 6 files changed, 108 insertions(+) create mode 100644 src/Ast/PhpDoc/PureUnlessParameterIsPassedTagValueNode.php diff --git a/src/Ast/PhpDoc/PhpDocNode.php b/src/Ast/PhpDoc/PhpDocNode.php index 6abad3d..4b17840 100644 --- a/src/Ast/PhpDoc/PhpDocNode.php +++ b/src/Ast/PhpDoc/PhpDocNode.php @@ -126,6 +126,17 @@ public function getPureUnlessCallableIsImpureTagValues(string $tagName = '@pure- ); } + /** + * @return PureUnlessParameterIsPassedTagValueNode[] + */ + public function getPureUnlessParameterIsPassedTagValues(string $tagName = '@pure-unless-parameter-passed'): array + { + return array_filter( + array_column($this->getTagsByName($tagName), 'value'), + static fn (PhpDocTagValueNode $value): bool => $value instanceof PureUnlessParameterIsPassedTagValueNode, + ); + } + /** * @return TemplateTagValueNode[] */ diff --git a/src/Ast/PhpDoc/PureUnlessParameterIsPassedTagValueNode.php b/src/Ast/PhpDoc/PureUnlessParameterIsPassedTagValueNode.php new file mode 100644 index 0000000..a49f5fd --- /dev/null +++ b/src/Ast/PhpDoc/PureUnlessParameterIsPassedTagValueNode.php @@ -0,0 +1,29 @@ +parameterName = $parameterName; + $this->description = $description; + } + + public function __toString(): string + { + return trim("{$this->parameterName} {$this->description}"); + } + +} diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php index ac717b9..7a63a4c 100644 --- a/src/Parser/PhpDocParser.php +++ b/src/Parser/PhpDocParser.php @@ -358,6 +358,11 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph $tagValue = $this->parsePureUnlessCallableIsImpureTagValue($tokens); break; + case '@pure-unless-parameter-passed': + case '@phpstan-pure-unless-parameter-passed': + $tagValue = $this->parsePureUnlessParameterIsPassed($tokens); + break; + case '@var': case '@phpstan-var': case '@psalm-var': @@ -878,6 +883,14 @@ private function parsePureUnlessCallableIsImpureTagValue(TokenIterator $tokens): return new Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode($parameterName, $description); } + private function parsePureUnlessParameterIsPassed(TokenIterator $tokens): Ast\PhpDoc\PureUnlessParameterIsPassedTagValueNode + { + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens, false); + + return new Ast\PhpDoc\PureUnlessParameterIsPassedTagValueNode($parameterName, $description); + } + private function parseVarTagValue(TokenIterator $tokens): Ast\PhpDoc\VarTagValueNode { $type = $this->typeParser->parse($tokens); diff --git a/src/Printer/Printer.php b/src/Printer/Printer.php index 8febe82..5371620 100644 --- a/src/Printer/Printer.php +++ b/src/Printer/Printer.php @@ -32,6 +32,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessParameterIsPassedTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\RequireExtendsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\RequireImplementsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; @@ -346,6 +347,9 @@ private function printTagValue(PhpDocTagValueNode $node): string if ($node instanceof PureUnlessCallableIsImpureTagValueNode) { return trim("{$node->parameterName} {$node->description}"); } + if ($node instanceof PureUnlessParameterIsPassedTagValueNode) { + return trim("{$node->parameterName} {$node->description}"); + } if ($node instanceof PropertyTagValueNode) { $type = $this->printType($node->type); return trim("{$type} {$node->propertyName} {$node->description}"); diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index 7626580..c1d2054 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -39,6 +39,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessParameterIsPassedTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\RequireExtendsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\RequireImplementsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; @@ -753,6 +754,37 @@ public function providePureUnlessCallableIsImpureTagsData(): Iterator ]; } + public function providePureUnlessParameterIsPassedTagsData(): Iterator + { + yield [ + 'OK', + '/** @pure-unless-parameter-passed $foo */', + new PhpDocNode([ + new PhpDocTagNode( + '@pure-unless-parameter-passed', + new PureUnlessParameterIsPassedTagValueNode( + '$foo', + '', + ), + ), + ]), + ]; + + yield [ + 'OK with description', + '/** @pure-unless-parameter-passed $foo test two three */', + new PhpDocNode([ + new PhpDocTagNode( + '@pure-unless-parameter-passed', + new PureUnlessParameterIsPassedTagValueNode( + '$foo', + 'test two three', + ), + ), + ]), + ]; + } + public function provideVarTagsData(): Iterator { yield [ diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php index 7c0e7a9..13ee9e9 100644 --- a/tests/PHPStan/Printer/PrinterTest.php +++ b/tests/PHPStan/Printer/PrinterTest.php @@ -24,6 +24,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessParameterIsPassedTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode; @@ -1788,6 +1789,24 @@ public function enterNode(Node $node) }, ]; + yield [ + '/** @pure-unless-parameter-passed $foo test */', + '/** @pure-unless-parameter-passed $bar foo */', + new class extends AbstractNodeVisitor { + + public function enterNode(Node $node) + { + if ($node instanceof PureUnlessParameterIsPassedTagValueNode) { + $node->parameterName = '$bar'; + $node->description = 'foo'; + } + + return $node; + } + + }, + ]; + yield [ '/** @return Foo[abc] */', '/** @return self::FOO[abc] */',