Skip to content

Commit

Permalink
Support for @param-immediately-invoked-callable and `@param-later-i…
Browse files Browse the repository at this point in the history
…nvoked-callable`
  • Loading branch information
ondrejmirtes committed Mar 21, 2024
1 parent 231e318 commit 8ce0d65
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 0 deletions.
30 changes: 30 additions & 0 deletions src/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);

namespace PHPStan\PhpDocParser\Ast\PhpDoc;

use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function trim;

class ParamImmediatelyInvokedCallableTagValueNode implements PhpDocTagValueNode
{

use NodeAttributes;

/** @var string */
public $parameterName;

/** @var string (may be empty) */
public $description;

public function __construct(string $parameterName, string $description)
{
$this->parameterName = $parameterName;
$this->description = $description;
}

public function __toString(): string
{
return trim("{$this->parameterName} {$this->description}");
}

}
30 changes: 30 additions & 0 deletions src/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);

namespace PHPStan\PhpDocParser\Ast\PhpDoc;

use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function trim;

class ParamLaterInvokedCallableTagValueNode implements PhpDocTagValueNode
{

use NodeAttributes;

/** @var string */
public $parameterName;

/** @var string (may be empty) */
public $description;

public function __construct(string $parameterName, string $description)
{
$this->parameterName = $parameterName;
$this->description = $description;
}

public function __toString(): string
{
return trim("{$this->parameterName} {$this->description}");
}

}
28 changes: 28 additions & 0 deletions src/Ast/PhpDoc/PhpDocNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,34 @@ static function (PhpDocTagValueNode $value): bool {
}


/**
* @return ParamImmediatelyInvokedCallableTagValueNode[]
*/
public function getParamImmediatelyInvokedCallableTagValues(string $tagName = '@param-immediately-invoked-callable'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof ParamImmediatelyInvokedCallableTagValueNode;
}
);
}


/**
* @return ParamLaterInvokedCallableTagValueNode[]
*/
public function getParamLaterInvokedCallableTagValues(string $tagName = '@param-later-invoked-callable'): array
{
return array_filter(
array_column($this->getTagsByName($tagName), 'value'),
static function (PhpDocTagValueNode $value): bool {
return $value instanceof ParamLaterInvokedCallableTagValueNode;
}
);
}


/**
* @return TemplateTagValueNode[]
*/
Expand Down
28 changes: 28 additions & 0 deletions src/Parser/PhpDocParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,16 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
$tagValue = $this->parseParamTagValue($tokens);
break;

case '@param-immediately-invoked-callable':
case '@phpstan-param-immediately-invoked-callable':
$tagValue = $this->parseParamImmediatelyInvokedCallableTagValue($tokens);
break;

case '@param-later-invoked-callable':
case '@phpstan-param-later-invoked-callable':
$tagValue = $this->parseParamLaterInvokedCallableTagValue($tokens);
break;

case '@var':
case '@phpstan-var':
case '@psalm-var':
Expand Down Expand Up @@ -861,6 +871,24 @@ private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTag
}


private function parseParamImmediatelyInvokedCallableTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode
{
$parameterName = $this->parseRequiredVariableName($tokens);
$description = $this->parseOptionalDescription($tokens);

return new Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode($parameterName, $description);
}


private function parseParamLaterInvokedCallableTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode
{
$parameterName = $this->parseRequiredVariableName($tokens);
$description = $this->parseOptionalDescription($tokens);

return new Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode($parameterName, $description);
}


private function parseVarTagValue(TokenIterator $tokens): Ast\PhpDoc\VarTagValueNode
{
$type = $this->typeParser->parse($tokens);
Expand Down
8 changes: 8 additions & 0 deletions src/Printer/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamOutTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
Expand Down Expand Up @@ -304,6 +306,12 @@ private function printTagValue(PhpDocTagValueNode $node): string
$type = $this->printType($node->type);
return trim("{$type} {$reference}{$variadic}{$node->parameterName} {$node->description}");
}
if ($node instanceof ParamImmediatelyInvokedCallableTagValueNode) {
return trim("{$node->parameterName} {$node->description}");
}
if ($node instanceof ParamLaterInvokedCallableTagValueNode) {
return trim("{$node->parameterName} {$node->description}");
}
if ($node instanceof PropertyTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->propertyName} {$node->description}");
Expand Down
68 changes: 68 additions & 0 deletions tests/PHPStan/Parser/PhpDocParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamOutTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
Expand Down Expand Up @@ -97,6 +99,8 @@ protected function setUp(): void
* @dataProvider provideTagsWithNumbers
* @dataProvider provideSpecializedTags
* @dataProvider provideParamTagsData
* @dataProvider provideParamImmediatelyInvokedCallableTagsData
* @dataProvider provideParamLaterInvokedCallableTagsData
* @dataProvider provideTypelessParamTagsData
* @dataProvider provideVarTagsData
* @dataProvider provideReturnTagsData
Expand Down Expand Up @@ -620,6 +624,68 @@ public function provideTypelessParamTagsData(): Iterator
];
}

public function provideParamImmediatelyInvokedCallableTagsData(): Iterator
{
yield [
'OK',
'/** @param-immediately-invoked-callable $foo */',
new PhpDocNode([
new PhpDocTagNode(
'@param-immediately-invoked-callable',
new ParamImmediatelyInvokedCallableTagValueNode(
'$foo',
''
)
),
]),
];

yield [
'OK with description',
'/** @param-immediately-invoked-callable $foo test two three */',
new PhpDocNode([
new PhpDocTagNode(
'@param-immediately-invoked-callable',
new ParamImmediatelyInvokedCallableTagValueNode(
'$foo',
'test two three'
)
),
]),
];
}

public function provideParamLaterInvokedCallableTagsData(): Iterator
{
yield [
'OK',
'/** @param-later-invoked-callable $foo */',
new PhpDocNode([
new PhpDocTagNode(
'@param-later-invoked-callable',
new ParamLaterInvokedCallableTagValueNode(
'$foo',
''
)
),
]),
];

yield [
'OK with description',
'/** @param-later-invoked-callable $foo test two three */',
new PhpDocNode([
new PhpDocTagNode(
'@param-later-invoked-callable',
new ParamLaterInvokedCallableTagValueNode(
'$foo',
'test two three'
)
),
]),
];
}

public function provideVarTagsData(): Iterator
{
yield [
Expand Down Expand Up @@ -7117,6 +7183,8 @@ public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): voi
* @dataProvider provideSpecializedTags
* @dataProvider provideParamTagsData
* @dataProvider provideTypelessParamTagsData
* @dataProvider provideParamImmediatelyInvokedCallableTagsData
* @dataProvider provideParamLaterInvokedCallableTagsData
* @dataProvider provideVarTagsData
* @dataProvider provideReturnTagsData
* @dataProvider provideThrowsTagsData
Expand Down
38 changes: 38 additions & 0 deletions tests/PHPStan/Printer/PrinterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArray;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArrayItem;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
Expand Down Expand Up @@ -1665,6 +1667,42 @@ public function enterNode(Node $node)

},
];

yield [
'/** @param-immediately-invoked-callable $foo test */',
'/** @param-immediately-invoked-callable $bar foo */',
new class extends AbstractNodeVisitor {

public function enterNode(Node $node)
{
if ($node instanceof ParamImmediatelyInvokedCallableTagValueNode) {
$node->parameterName = '$bar';
$node->description = 'foo';
}

return $node;
}

},
];

yield [
'/** @param-later-invoked-callable $foo test */',
'/** @param-later-invoked-callable $bar foo */',
new class extends AbstractNodeVisitor {

public function enterNode(Node $node)
{
if ($node instanceof ParamLaterInvokedCallableTagValueNode) {
$node->parameterName = '$bar';
$node->description = 'foo';
}

return $node;
}

},
];
}

/**
Expand Down

0 comments on commit 8ce0d65

Please sign in to comment.