Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/Ast/PhpDoc/ParamClosureScopeTagValueNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?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 ParamClosureScopeTagValueNode implements PhpDocTagValueNode
{

use NodeAttributes;

public TypeNode $type;

public string $parameterName;

/** @var string (may be empty) */
public string $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}");
}

/**
* @param array<string, mixed> $properties
*/
public static function __set_state(array $properties): self
{
$instance = new self($properties['type'], $properties['parameterName'], $properties['description']);
if (isset($properties['attributes'])) {
foreach ($properties['attributes'] as $key => $value) {
$instance->setAttribute($key, $value);
}
}
return $instance;
}

}
11 changes: 11 additions & 0 deletions src/Ast/PhpDoc/PhpDocNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@ public function getParamClosureThisTagValues(string $tagName = '@param-closure-t
);
}

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

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

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

case '@pure-unless-callable-is-impure':
case '@phpstan-pure-unless-callable-is-impure':
$tagValue = $this->parsePureUnlessCallableIsImpureTagValue($tokens);
Expand Down Expand Up @@ -874,6 +879,15 @@ private function parseParamClosureThisTagValue(TokenIterator $tokens): Ast\PhpDo
return new Ast\PhpDoc\ParamClosureThisTagValueNode($type, $parameterName, $description);
}

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

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

private function parsePureUnlessCallableIsImpureTagValue(TokenIterator $tokens): Ast\PhpDoc\PureUnlessCallableIsImpureTagValueNode
{
$parameterName = $this->parseRequiredVariableName($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 @@ -21,6 +21,7 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamClosureScopeTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamClosureThisTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
Expand Down Expand Up @@ -362,6 +363,9 @@ private function printTagValue(PhpDocTagValueNode $node): string
if ($node instanceof ParamClosureThisTagValueNode) {
return trim("{$node->type} {$node->parameterName} {$node->description}");
}
if ($node instanceof ParamClosureScopeTagValueNode) {
return trim("{$node->type} {$node->parameterName} {$node->description}");
}
if ($node instanceof PureUnlessCallableIsImpureTagValueNode) {
return trim("{$node->parameterName} {$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\ParamClosureScopeTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamClosureThisTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
Expand Down Expand Up @@ -103,6 +104,7 @@ protected function setUp(): void
* @dataProvider provideParamLaterInvokedCallableTagsData
* @dataProvider provideTypelessParamTagsData
* @dataProvider provideParamClosureThisTagsData
* @dataProvider provideParamClosureScopeTagsData
* @dataProvider providePureUnlessCallableIsImpureTagsData
* @dataProvider providePureUnlessParameterIsPassedTagsData
* @dataProvider provideVarTagsData
Expand Down Expand Up @@ -733,6 +735,54 @@ public function provideParamClosureThisTagsData(): Iterator
];
}

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

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

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

public function providePureUnlessCallableIsImpureTagsData(): Iterator
{
yield [
Expand Down Expand Up @@ -7829,6 +7879,7 @@ public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): voi
* @dataProvider provideParamImmediatelyInvokedCallableTagsData
* @dataProvider provideParamLaterInvokedCallableTagsData
* @dataProvider provideParamClosureThisTagsData
* @dataProvider provideParamClosureScopeTagsData
* @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 @@ -18,6 +18,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\ParamClosureScopeTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamClosureThisTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
Expand Down Expand Up @@ -2145,6 +2146,25 @@ public function enterNode(Node $node)
},
];

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

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

return $node;
}

},
];

yield [
'/** @pure-unless-callable-is-impure $foo test */',
'/** @pure-unless-callable-is-impure $bar foo */',
Expand Down
Loading