Skip to content

Commit 8ce0d65

Browse files
committed
Support for @param-immediately-invoked-callable and @param-later-invoked-callable
1 parent 231e318 commit 8ce0d65

7 files changed

+230
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
4+
5+
use PHPStan\PhpDocParser\Ast\NodeAttributes;
6+
use function trim;
7+
8+
class ParamImmediatelyInvokedCallableTagValueNode implements PhpDocTagValueNode
9+
{
10+
11+
use NodeAttributes;
12+
13+
/** @var string */
14+
public $parameterName;
15+
16+
/** @var string (may be empty) */
17+
public $description;
18+
19+
public function __construct(string $parameterName, string $description)
20+
{
21+
$this->parameterName = $parameterName;
22+
$this->description = $description;
23+
}
24+
25+
public function __toString(): string
26+
{
27+
return trim("{$this->parameterName} {$this->description}");
28+
}
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
4+
5+
use PHPStan\PhpDocParser\Ast\NodeAttributes;
6+
use function trim;
7+
8+
class ParamLaterInvokedCallableTagValueNode implements PhpDocTagValueNode
9+
{
10+
11+
use NodeAttributes;
12+
13+
/** @var string */
14+
public $parameterName;
15+
16+
/** @var string (may be empty) */
17+
public $description;
18+
19+
public function __construct(string $parameterName, string $description)
20+
{
21+
$this->parameterName = $parameterName;
22+
$this->description = $description;
23+
}
24+
25+
public function __toString(): string
26+
{
27+
return trim("{$this->parameterName} {$this->description}");
28+
}
29+
30+
}

src/Ast/PhpDoc/PhpDocNode.php

+28
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,34 @@ static function (PhpDocTagValueNode $value): bool {
9090
}
9191

9292

93+
/**
94+
* @return ParamImmediatelyInvokedCallableTagValueNode[]
95+
*/
96+
public function getParamImmediatelyInvokedCallableTagValues(string $tagName = '@param-immediately-invoked-callable'): array
97+
{
98+
return array_filter(
99+
array_column($this->getTagsByName($tagName), 'value'),
100+
static function (PhpDocTagValueNode $value): bool {
101+
return $value instanceof ParamImmediatelyInvokedCallableTagValueNode;
102+
}
103+
);
104+
}
105+
106+
107+
/**
108+
* @return ParamLaterInvokedCallableTagValueNode[]
109+
*/
110+
public function getParamLaterInvokedCallableTagValues(string $tagName = '@param-later-invoked-callable'): array
111+
{
112+
return array_filter(
113+
array_column($this->getTagsByName($tagName), 'value'),
114+
static function (PhpDocTagValueNode $value): bool {
115+
return $value instanceof ParamLaterInvokedCallableTagValueNode;
116+
}
117+
);
118+
}
119+
120+
93121
/**
94122
* @return TemplateTagValueNode[]
95123
*/

src/Parser/PhpDocParser.php

+28
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,16 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
387387
$tagValue = $this->parseParamTagValue($tokens);
388388
break;
389389

390+
case '@param-immediately-invoked-callable':
391+
case '@phpstan-param-immediately-invoked-callable':
392+
$tagValue = $this->parseParamImmediatelyInvokedCallableTagValue($tokens);
393+
break;
394+
395+
case '@param-later-invoked-callable':
396+
case '@phpstan-param-later-invoked-callable':
397+
$tagValue = $this->parseParamLaterInvokedCallableTagValue($tokens);
398+
break;
399+
390400
case '@var':
391401
case '@phpstan-var':
392402
case '@psalm-var':
@@ -861,6 +871,24 @@ private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTag
861871
}
862872

863873

874+
private function parseParamImmediatelyInvokedCallableTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode
875+
{
876+
$parameterName = $this->parseRequiredVariableName($tokens);
877+
$description = $this->parseOptionalDescription($tokens);
878+
879+
return new Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode($parameterName, $description);
880+
}
881+
882+
883+
private function parseParamLaterInvokedCallableTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode
884+
{
885+
$parameterName = $this->parseRequiredVariableName($tokens);
886+
$description = $this->parseOptionalDescription($tokens);
887+
888+
return new Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode($parameterName, $description);
889+
}
890+
891+
864892
private function parseVarTagValue(TokenIterator $tokens): Ast\PhpDoc\VarTagValueNode
865893
{
866894
$type = $this->typeParser->parse($tokens);

src/Printer/Printer.php

+8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
2121
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
2222
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
23+
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
24+
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
2325
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamOutTagValueNode;
2426
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
2527
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
@@ -304,6 +306,12 @@ private function printTagValue(PhpDocTagValueNode $node): string
304306
$type = $this->printType($node->type);
305307
return trim("{$type} {$reference}{$variadic}{$node->parameterName} {$node->description}");
306308
}
309+
if ($node instanceof ParamImmediatelyInvokedCallableTagValueNode) {
310+
return trim("{$node->parameterName} {$node->description}");
311+
}
312+
if ($node instanceof ParamLaterInvokedCallableTagValueNode) {
313+
return trim("{$node->parameterName} {$node->description}");
314+
}
307315
if ($node instanceof PropertyTagValueNode) {
308316
$type = $this->printType($node->type);
309317
return trim("{$type} {$node->propertyName} {$node->description}");

tests/PHPStan/Parser/PhpDocParserTest.php

+68
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
3030
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
3131
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
32+
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
33+
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
3234
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamOutTagValueNode;
3335
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
3436
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
@@ -97,6 +99,8 @@ protected function setUp(): void
9799
* @dataProvider provideTagsWithNumbers
98100
* @dataProvider provideSpecializedTags
99101
* @dataProvider provideParamTagsData
102+
* @dataProvider provideParamImmediatelyInvokedCallableTagsData
103+
* @dataProvider provideParamLaterInvokedCallableTagsData
100104
* @dataProvider provideTypelessParamTagsData
101105
* @dataProvider provideVarTagsData
102106
* @dataProvider provideReturnTagsData
@@ -620,6 +624,68 @@ public function provideTypelessParamTagsData(): Iterator
620624
];
621625
}
622626

627+
public function provideParamImmediatelyInvokedCallableTagsData(): Iterator
628+
{
629+
yield [
630+
'OK',
631+
'/** @param-immediately-invoked-callable $foo */',
632+
new PhpDocNode([
633+
new PhpDocTagNode(
634+
'@param-immediately-invoked-callable',
635+
new ParamImmediatelyInvokedCallableTagValueNode(
636+
'$foo',
637+
''
638+
)
639+
),
640+
]),
641+
];
642+
643+
yield [
644+
'OK with description',
645+
'/** @param-immediately-invoked-callable $foo test two three */',
646+
new PhpDocNode([
647+
new PhpDocTagNode(
648+
'@param-immediately-invoked-callable',
649+
new ParamImmediatelyInvokedCallableTagValueNode(
650+
'$foo',
651+
'test two three'
652+
)
653+
),
654+
]),
655+
];
656+
}
657+
658+
public function provideParamLaterInvokedCallableTagsData(): Iterator
659+
{
660+
yield [
661+
'OK',
662+
'/** @param-later-invoked-callable $foo */',
663+
new PhpDocNode([
664+
new PhpDocTagNode(
665+
'@param-later-invoked-callable',
666+
new ParamLaterInvokedCallableTagValueNode(
667+
'$foo',
668+
''
669+
)
670+
),
671+
]),
672+
];
673+
674+
yield [
675+
'OK with description',
676+
'/** @param-later-invoked-callable $foo test two three */',
677+
new PhpDocNode([
678+
new PhpDocTagNode(
679+
'@param-later-invoked-callable',
680+
new ParamLaterInvokedCallableTagValueNode(
681+
'$foo',
682+
'test two three'
683+
)
684+
),
685+
]),
686+
];
687+
}
688+
623689
public function provideVarTagsData(): Iterator
624690
{
625691
yield [
@@ -7117,6 +7183,8 @@ public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): voi
71177183
* @dataProvider provideSpecializedTags
71187184
* @dataProvider provideParamTagsData
71197185
* @dataProvider provideTypelessParamTagsData
7186+
* @dataProvider provideParamImmediatelyInvokedCallableTagsData
7187+
* @dataProvider provideParamLaterInvokedCallableTagsData
71207188
* @dataProvider provideVarTagsData
71217189
* @dataProvider provideReturnTagsData
71227190
* @dataProvider provideThrowsTagsData

tests/PHPStan/Printer/PrinterTest.php

+38
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArray;
1818
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArrayItem;
1919
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
20+
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
21+
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
2022
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
2123
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
2224
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
@@ -1665,6 +1667,42 @@ public function enterNode(Node $node)
16651667

16661668
},
16671669
];
1670+
1671+
yield [
1672+
'/** @param-immediately-invoked-callable $foo test */',
1673+
'/** @param-immediately-invoked-callable $bar foo */',
1674+
new class extends AbstractNodeVisitor {
1675+
1676+
public function enterNode(Node $node)
1677+
{
1678+
if ($node instanceof ParamImmediatelyInvokedCallableTagValueNode) {
1679+
$node->parameterName = '$bar';
1680+
$node->description = 'foo';
1681+
}
1682+
1683+
return $node;
1684+
}
1685+
1686+
},
1687+
];
1688+
1689+
yield [
1690+
'/** @param-later-invoked-callable $foo test */',
1691+
'/** @param-later-invoked-callable $bar foo */',
1692+
new class extends AbstractNodeVisitor {
1693+
1694+
public function enterNode(Node $node)
1695+
{
1696+
if ($node instanceof ParamLaterInvokedCallableTagValueNode) {
1697+
$node->parameterName = '$bar';
1698+
$node->description = 'foo';
1699+
}
1700+
1701+
return $node;
1702+
}
1703+
1704+
},
1705+
];
16681706
}
16691707

16701708
/**

0 commit comments

Comments
 (0)