Skip to content

Commit

Permalink
Implement this-out/self-out syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
rvanvelzen authored and ondrejmirtes committed Oct 6, 2022
1 parent 5eaedcd commit 22a7b24
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/Ast/PhpDoc/PhpDocNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,19 @@ static function (PhpDocTagValueNode $value): bool {
}


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

public function __toString(): string
{
$children = array_map(
Expand Down
32 changes: 32 additions & 0 deletions src/Ast/PhpDoc/SelfOutTagValueNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?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 SelfOutTagValueNode implements PhpDocTagValueNode
{

use NodeAttributes;

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

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

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


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

}
15 changes: 15 additions & 0 deletions src/Parser/PhpDocParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
$tagValue = $this->parseAssertTagValue($tokens);
break;

case '@phpstan-this-out':
case '@phpstan-self-out':
case '@psalm-this-out':
case '@psalm-self-out':
$tagValue = $this->parseSelfOutTagValue($tokens);
break;

default:
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
break;
Expand Down Expand Up @@ -499,6 +506,14 @@ private function parseAssertParameter(TokenIterator $tokens): array
return ['parameter' => $parameter];
}

private function parseSelfOutTagValue(TokenIterator $tokens): Ast\PhpDoc\SelfOutTagValueNode
{
$type = $this->typeParser->parse($tokens);
$description = $this->parseOptionalDescription($tokens);

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

private function parseOptionalVariableName(TokenIterator $tokens): string
{
if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) {
Expand Down
75 changes: 75 additions & 0 deletions tests/PHPStan/Parser/PhpDocParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\SelfOutTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode;
Expand Down Expand Up @@ -90,6 +91,7 @@ protected function setUp(): void
* @dataProvider provideRealWorldExampleData
* @dataProvider provideDescriptionWithOrWithoutHtml
* @dataProvider provideTagsWithBackslash
* @dataProvider provideSelfOutTagsData
*/
public function testParse(
string $label,
Expand Down Expand Up @@ -4485,6 +4487,79 @@ public function provideTagsWithBackslash(): Iterator
];
}

public function provideSelfOutTagsData(): Iterator
{
yield [
'OK phpstan-self-out',
'/** @phpstan-self-out self<T> */',
new PhpDocNode([
new PhpDocTagNode(
'@phpstan-self-out',
new SelfOutTagValueNode(
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
''
)
),
]),
];

yield [
'OK phpstan-this-out',
'/** @phpstan-this-out self<T> */',
new PhpDocNode([
new PhpDocTagNode(
'@phpstan-this-out',
new SelfOutTagValueNode(
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
''
)
),
]),
];

yield [
'OK psalm-self-out',
'/** @psalm-self-out self<T> */',
new PhpDocNode([
new PhpDocTagNode(
'@psalm-self-out',
new SelfOutTagValueNode(
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
''
)
),
]),
];

yield [
'OK psalm-this-out',
'/** @psalm-this-out self<T> */',
new PhpDocNode([
new PhpDocTagNode(
'@psalm-this-out',
new SelfOutTagValueNode(
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
''
)
),
]),
];

yield [
'OK with description',
'/** @phpstan-self-out self<T> description */',
new PhpDocNode([
new PhpDocTagNode(
'@phpstan-self-out',
new SelfOutTagValueNode(
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
'description'
)
),
]),
];
}

/**
* @dataProvider dataParseTagValue
* @param PhpDocNode $expectedPhpDocNode
Expand Down

0 comments on commit 22a7b24

Please sign in to comment.