diff --git a/src/Ast/PhpDoc/PhpDocNode.php b/src/Ast/PhpDoc/PhpDocNode.php index ade55b7..a08b777 100644 --- a/src/Ast/PhpDoc/PhpDocNode.php +++ b/src/Ast/PhpDoc/PhpDocNode.php @@ -131,6 +131,18 @@ static function (PhpDocTagValueNode $value): bool { ); } + /** + * @return PureUnlessCallableIsImpureTagValueNode[] + */ + public function getPureUnlessCallableIsImpureTagValues(string $tagName = '@pure-unless-callable-is-impure'): array + { + return array_filter( + array_column($this->getTagsByName($tagName), 'value'), + static function (PhpDocTagValueNode $value): bool { + return $value instanceof PureUnlessCallableIsImpureTagValueNode; + } + ); + } /** * @return TemplateTagValueNode[] diff --git a/src/Ast/PhpDoc/PureUnlessCallableIsImpureTagValueNode.php b/src/Ast/PhpDoc/PureUnlessCallableIsImpureTagValueNode.php new file mode 100644 index 0000000..4cf0937 --- /dev/null +++ b/src/Ast/PhpDoc/PureUnlessCallableIsImpureTagValueNode.php @@ -0,0 +1,30 @@ +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 f6ef5db..b6cd85e 100644 --- a/src/Parser/PhpDocParser.php +++ b/src/Parser/PhpDocParser.php @@ -403,6 +403,11 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph $tagValue = $this->parseParamClosureThisTagValue($tokens); break; + case '@pure-unless-callable-is-impure': + case '@phpstan-pure-unless-callable-is-impure': + $tagValue = $this->parsePureUnlessCallableIsImpureTagValue($tokens); + break; + case '@var': case '@phpstan-var': case '@psalm-var': @@ -919,6 +924,13 @@ private function parseParamClosureThisTagValue(TokenIterator $tokens): Ast\PhpDo return new Ast\PhpDoc\ParamClosureThisTagValueNode($type, $parameterName, $description); } + private function parsePureUnlessCallableIsImpureTagValue(TokenIterator $tokens): Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode + { + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens); + + return new Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode($parameterName, $description); + } private function parseVarTagValue(TokenIterator $tokens): Ast\PhpDoc\VarTagValueNode { diff --git a/src/Printer/Printer.php b/src/Printer/Printer.php index 7550078..f666598 100644 --- a/src/Printer/Printer.php +++ b/src/Printer/Printer.php @@ -31,6 +31,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\RequireExtendsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\RequireImplementsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; @@ -322,6 +323,9 @@ private function printTagValue(PhpDocTagValueNode $node): string if ($node instanceof ParamClosureThisTagValueNode) { return trim("{$node->type} {$node->parameterName} {$node->description}"); } + if ($node instanceof PureUnlessCallableIsImpureTagValueNode) { + 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 f1efa5f..1017fd3 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -38,6 +38,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\RequireExtendsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\RequireImplementsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; @@ -104,6 +105,7 @@ protected function setUp(): void * @dataProvider provideParamLaterInvokedCallableTagsData * @dataProvider provideTypelessParamTagsData * @dataProvider provideParamClosureThisTagsData + * @dataProvider providePureUnlessCallableIsImpureTagsData * @dataProvider provideVarTagsData * @dataProvider provideReturnTagsData * @dataProvider provideThrowsTagsData @@ -736,6 +738,37 @@ public function provideParamClosureThisTagsData(): Iterator ]; } + public function providePureUnlessCallableIsImpureTagsData(): Iterator + { + yield [ + 'OK', + '/** @pure-unless-callable-is-impure $foo */', + new PhpDocNode([ + new PhpDocTagNode( + '@pure-unless-callable-is-impure', + new PureUnlessCallableIsImpureTagValueNode( + '$foo', + '' + ) + ), + ]), + ]; + + yield [ + 'OK with description', + '/** @pure-unless-callable-is-impure $foo test two three */', + new PhpDocNode([ + new PhpDocTagNode( + '@pure-unless-callable-is-impure', + new PureUnlessCallableIsImpureTagValueNode( + '$foo', + 'test two three' + ) + ), + ]), + ]; + } + public function provideVarTagsData(): Iterator { yield [ diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php index ee549c7..e4effc3 100644 --- a/tests/PHPStan/Printer/PrinterTest.php +++ b/tests/PHPStan/Printer/PrinterTest.php @@ -23,6 +23,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode; @@ -1782,6 +1783,24 @@ public function enterNode(Node $node) }, ]; + yield [ + '/** @pure-unless-callable-is-impure $foo test */', + '/** @pure-unless-callable-is-impure $bar foo */', + new class extends AbstractNodeVisitor { + + public function enterNode(Node $node) + { + if ($node instanceof PureUnlessCallableIsImpureTagValueNode) { + $node->parameterName = '$bar'; + $node->description = 'foo'; + } + + return $node; + } + + }, + ]; + yield [ '/** @return Foo[abc] */', '/** @return self::FOO[abc] */',