Skip to content

Commit

Permalink
Support for @param-closure-this
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Mar 21, 2024
1 parent 8ce0d65 commit 86e4d5a
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/Ast/PhpDoc/ParamClosureThisTagValueNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types = 1);

namespace PHPStan\PhpDocParser\Ast\PhpDoc;

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

class ParamClosureThisTagValueNode implements PhpDocTagValueNode
{

use NodeAttributes;

/** @var TypeNode */
public $type;

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

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

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

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

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


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


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

case '@param-closure-this':
case '@phpstan-param-closure-this':
$tagValue = $this->parseParamClosureThisTagValue($tokens);
break;

case '@var':
case '@phpstan-var':
case '@psalm-var':
Expand Down Expand Up @@ -889,6 +894,16 @@ private function parseParamLaterInvokedCallableTagValue(TokenIterator $tokens):
}


private function parseParamClosureThisTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamClosureThisTagValueNode
{
$type = $this->typeParser->parse($tokens);
$parameterName = $this->parseRequiredVariableName($tokens);
$description = $this->parseOptionalDescription($tokens);

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


private function parseVarTagValue(TokenIterator $tokens): Ast\PhpDoc\VarTagValueNode
{
$type = $this->typeParser->parse($tokens);
Expand Down
4 changes: 4 additions & 0 deletions src/Printer/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamClosureThisTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamOutTagValueNode;
Expand Down Expand Up @@ -312,6 +313,9 @@ private function printTagValue(PhpDocTagValueNode $node): string
if ($node instanceof ParamLaterInvokedCallableTagValueNode) {
return trim("{$node->parameterName} {$node->description}");
}
if ($node instanceof ParamClosureThisTagValueNode) {
return trim("{$node->type} {$node->parameterName} {$node->description}");
}
if ($node instanceof PropertyTagValueNode) {
$type = $this->printType($node->type);
return trim("{$type} {$node->propertyName} {$node->description}");
Expand Down
51 changes: 51 additions & 0 deletions tests/PHPStan/Parser/PhpDocParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamClosureThisTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamOutTagValueNode;
Expand Down Expand Up @@ -102,6 +103,7 @@ protected function setUp(): void
* @dataProvider provideParamImmediatelyInvokedCallableTagsData
* @dataProvider provideParamLaterInvokedCallableTagsData
* @dataProvider provideTypelessParamTagsData
* @dataProvider provideParamClosureThisTagsData
* @dataProvider provideVarTagsData
* @dataProvider provideReturnTagsData
* @dataProvider provideThrowsTagsData
Expand Down Expand Up @@ -686,6 +688,54 @@ public function provideParamLaterInvokedCallableTagsData(): Iterator
];
}

public function provideParamClosureThisTagsData(): Iterator
{
yield [
'OK',
'/** @param-closure-this Foo $a */',
new PhpDocNode([
new PhpDocTagNode(
'@param-closure-this',
new ParamClosureThisTagValueNode(
new IdentifierTypeNode('Foo'),
'$a',
''
)
),
]),
];

yield [
'OK with prefix',
'/** @phpstan-param-closure-this Foo $a */',
new PhpDocNode([
new PhpDocTagNode(
'@phpstan-param-closure-this',
new ParamClosureThisTagValueNode(
new IdentifierTypeNode('Foo'),
'$a',
''
)
),
]),
];

yield [
'OK with description',
'/** @param-closure-this Foo $a test */',
new PhpDocNode([
new PhpDocTagNode(
'@param-closure-this',
new ParamClosureThisTagValueNode(
new IdentifierTypeNode('Foo'),
'$a',
'test'
)
),
]),
];
}

public function provideVarTagsData(): Iterator
{
yield [
Expand Down Expand Up @@ -7185,6 +7235,7 @@ public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): voi
* @dataProvider provideTypelessParamTagsData
* @dataProvider provideParamImmediatelyInvokedCallableTagsData
* @dataProvider provideParamLaterInvokedCallableTagsData
* @dataProvider provideParamClosureThisTagsData
* @dataProvider provideVarTagsData
* @dataProvider provideReturnTagsData
* @dataProvider provideThrowsTagsData
Expand Down
20 changes: 20 additions & 0 deletions tests/PHPStan/Printer/PrinterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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\ParamClosureThisTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
Expand Down Expand Up @@ -1703,6 +1704,25 @@ public function enterNode(Node $node)

},
];

yield [
'/** @param-closure-this Foo $test haha */',
'/** @param-closure-this Bar $taste hehe */',
new class extends AbstractNodeVisitor {

public function enterNode(Node $node)
{
if ($node instanceof ParamClosureThisTagValueNode) {
$node->type = new IdentifierTypeNode('Bar');
$node->parameterName = '$taste';
$node->description = 'hehe';
}

return $node;
}

},
];
}

/**
Expand Down

0 comments on commit 86e4d5a

Please sign in to comment.