Skip to content

Commit 22a7b24

Browse files
rvanvelzenondrejmirtes
authored andcommittedOct 6, 2022
Implement this-out/self-out syntax
1 parent 5eaedcd commit 22a7b24

File tree

4 files changed

+135
-0
lines changed

4 files changed

+135
-0
lines changed
 

Diff for: ‎src/Ast/PhpDoc/PhpDocNode.php

+13
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,19 @@ static function (PhpDocTagValueNode $value): bool {
328328
}
329329

330330

331+
/**
332+
* @return SelfOutTagValueNode[]
333+
*/
334+
public function getSelfOutTypeTagValues(string $tagName = '@phpstan-this-out'): array
335+
{
336+
return array_filter(
337+
array_column($this->getTagsByName($tagName), 'value'),
338+
static function (PhpDocTagValueNode $value): bool {
339+
return $value instanceof SelfOutTagValueNode;
340+
}
341+
);
342+
}
343+
331344
public function __toString(): string
332345
{
333346
$children = array_map(

Diff for: ‎src/Ast/PhpDoc/SelfOutTagValueNode.php

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

Diff for: ‎src/Parser/PhpDocParser.php

+15
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
222222
$tagValue = $this->parseAssertTagValue($tokens);
223223
break;
224224

225+
case '@phpstan-this-out':
226+
case '@phpstan-self-out':
227+
case '@psalm-this-out':
228+
case '@psalm-self-out':
229+
$tagValue = $this->parseSelfOutTagValue($tokens);
230+
break;
231+
225232
default:
226233
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
227234
break;
@@ -499,6 +506,14 @@ private function parseAssertParameter(TokenIterator $tokens): array
499506
return ['parameter' => $parameter];
500507
}
501508

509+
private function parseSelfOutTagValue(TokenIterator $tokens): Ast\PhpDoc\SelfOutTagValueNode
510+
{
511+
$type = $this->typeParser->parse($tokens);
512+
$description = $this->parseOptionalDescription($tokens);
513+
514+
return new Ast\PhpDoc\SelfOutTagValueNode($type, $description);
515+
}
516+
502517
private function parseOptionalVariableName(TokenIterator $tokens): string
503518
{
504519
if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) {

Diff for: ‎tests/PHPStan/Parser/PhpDocParserTest.php

+75
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
2626
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
2727
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
28+
use PHPStan\PhpDocParser\Ast\PhpDoc\SelfOutTagValueNode;
2829
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
2930
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
3031
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode;
@@ -90,6 +91,7 @@ protected function setUp(): void
9091
* @dataProvider provideRealWorldExampleData
9192
* @dataProvider provideDescriptionWithOrWithoutHtml
9293
* @dataProvider provideTagsWithBackslash
94+
* @dataProvider provideSelfOutTagsData
9395
*/
9496
public function testParse(
9597
string $label,
@@ -4485,6 +4487,79 @@ public function provideTagsWithBackslash(): Iterator
44854487
];
44864488
}
44874489

4490+
public function provideSelfOutTagsData(): Iterator
4491+
{
4492+
yield [
4493+
'OK phpstan-self-out',
4494+
'/** @phpstan-self-out self<T> */',
4495+
new PhpDocNode([
4496+
new PhpDocTagNode(
4497+
'@phpstan-self-out',
4498+
new SelfOutTagValueNode(
4499+
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
4500+
''
4501+
)
4502+
),
4503+
]),
4504+
];
4505+
4506+
yield [
4507+
'OK phpstan-this-out',
4508+
'/** @phpstan-this-out self<T> */',
4509+
new PhpDocNode([
4510+
new PhpDocTagNode(
4511+
'@phpstan-this-out',
4512+
new SelfOutTagValueNode(
4513+
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
4514+
''
4515+
)
4516+
),
4517+
]),
4518+
];
4519+
4520+
yield [
4521+
'OK psalm-self-out',
4522+
'/** @psalm-self-out self<T> */',
4523+
new PhpDocNode([
4524+
new PhpDocTagNode(
4525+
'@psalm-self-out',
4526+
new SelfOutTagValueNode(
4527+
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
4528+
''
4529+
)
4530+
),
4531+
]),
4532+
];
4533+
4534+
yield [
4535+
'OK psalm-this-out',
4536+
'/** @psalm-this-out self<T> */',
4537+
new PhpDocNode([
4538+
new PhpDocTagNode(
4539+
'@psalm-this-out',
4540+
new SelfOutTagValueNode(
4541+
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
4542+
''
4543+
)
4544+
),
4545+
]),
4546+
];
4547+
4548+
yield [
4549+
'OK with description',
4550+
'/** @phpstan-self-out self<T> description */',
4551+
new PhpDocNode([
4552+
new PhpDocTagNode(
4553+
'@phpstan-self-out',
4554+
new SelfOutTagValueNode(
4555+
new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')]),
4556+
'description'
4557+
)
4558+
),
4559+
]),
4560+
];
4561+
}
4562+
44884563
/**
44894564
* @dataProvider dataParseTagValue
44904565
* @param PhpDocNode $expectedPhpDocNode

0 commit comments

Comments
 (0)