Skip to content

Commit 59719bc

Browse files
rvanvelzenondrejmirtes
authored andcommitted
Add property/method as separate tag nodes
1 parent ed3239d commit 59719bc

File tree

5 files changed

+154
-14
lines changed

5 files changed

+154
-14
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 AssertTagMethodValueNode implements PhpDocTagValueNode
10+
{
11+
12+
use NodeAttributes;
13+
14+
/** @var TypeNode */
15+
public $type;
16+
17+
/** @var string */
18+
public $parameter;
19+
20+
/** @var string */
21+
public $method;
22+
23+
/** @var bool */
24+
public $isNegated;
25+
26+
/** @var string (may be empty) */
27+
public $description;
28+
29+
public function __construct(TypeNode $type, string $parameter, string $method, bool $isNegated, string $description)
30+
{
31+
$this->type = $type;
32+
$this->parameter = $parameter;
33+
$this->method = $method;
34+
$this->isNegated = $isNegated;
35+
$this->description = $description;
36+
}
37+
38+
39+
public function __toString(): string
40+
{
41+
$isNegated = $this->isNegated ? '!' : '';
42+
return trim("{$this->type} {$isNegated}{$this->parameter}->{$this->method}() {$this->description}");
43+
}
44+
45+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 AssertTagPropertyValueNode implements PhpDocTagValueNode
10+
{
11+
12+
use NodeAttributes;
13+
14+
/** @var TypeNode */
15+
public $type;
16+
17+
/** @var string */
18+
public $parameter;
19+
20+
/** @var string */
21+
public $property;
22+
23+
/** @var bool */
24+
public $isNegated;
25+
26+
/** @var string (may be empty) */
27+
public $description;
28+
29+
public function __construct(TypeNode $type, string $parameter, string $property, bool $isNegated, string $description)
30+
{
31+
$this->type = $type;
32+
$this->parameter = $parameter;
33+
$this->property = $property;
34+
$this->isNegated = $isNegated;
35+
$this->description = $description;
36+
}
37+
38+
39+
public function __toString(): string
40+
{
41+
$isNegated = $this->isNegated ? '!' : '';
42+
return trim("{$this->type} {$isNegated}{$this->parameter}->{$this->property} {$this->description}");
43+
}
44+
45+
}

src/Ast/PhpDoc/PhpDocNode.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,34 @@ static function (PhpDocTagValueNode $value): bool {
300300
}
301301

302302

303+
/**
304+
* @return AssertTagPropertyValueNode[]
305+
*/
306+
public function getAssertPropertyTagValues(string $tagName = '@phpstan-assert'): array
307+
{
308+
return array_filter(
309+
array_column($this->getTagsByName($tagName), 'value'),
310+
static function (PhpDocTagValueNode $value): bool {
311+
return $value instanceof AssertTagPropertyValueNode;
312+
}
313+
);
314+
}
315+
316+
317+
/**
318+
* @return AssertTagMethodValueNode[]
319+
*/
320+
public function getAssertMethodTagValues(string $tagName = '@phpstan-assert'): array
321+
{
322+
return array_filter(
323+
array_column($this->getTagsByName($tagName), 'value'),
324+
static function (PhpDocTagValueNode $value): bool {
325+
return $value instanceof AssertTagMethodValueNode;
326+
}
327+
);
328+
}
329+
330+
303331
public function __toString(): string
304332
{
305333
$children = array_map(

src/Parser/PhpDocParser.php

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
77
use PHPStan\PhpDocParser\Lexer\Lexer;
88
use PHPStan\ShouldNotHappenException;
9+
use function array_key_exists;
910
use function array_values;
1011
use function count;
1112
use function trim;
@@ -446,16 +447,29 @@ private function parseTypeAliasImportTagValue(TokenIterator $tokens): Ast\PhpDoc
446447
return new Ast\PhpDoc\TypeAliasImportTagValueNode($importedAlias, new IdentifierTypeNode($importedFrom), $importedAs);
447448
}
448449

449-
private function parseAssertTagValue(TokenIterator $tokens): Ast\PhpDoc\AssertTagValueNode
450+
/**
451+
* @return Ast\PhpDoc\AssertTagValueNode|Ast\PhpDoc\AssertTagPropertyValueNode|Ast\PhpDoc\AssertTagMethodValueNode
452+
*/
453+
private function parseAssertTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode
450454
{
451455
$isNegated = $tokens->tryConsumeTokenType(Lexer::TOKEN_NEGATED);
452456
$type = $this->typeParser->parse($tokens);
453457
$parameter = $this->parseAssertParameter($tokens);
454458
$description = $this->parseOptionalDescription($tokens);
455-
return new Ast\PhpDoc\AssertTagValueNode($type, $parameter, $isNegated, $description);
459+
460+
if (array_key_exists('method', $parameter)) {
461+
return new Ast\PhpDoc\AssertTagMethodValueNode($type, $parameter['parameter'], $parameter['method'], $isNegated, $description);
462+
} elseif (array_key_exists('property', $parameter)) {
463+
return new Ast\PhpDoc\AssertTagPropertyValueNode($type, $parameter['parameter'], $parameter['property'], $isNegated, $description);
464+
}
465+
466+
return new Ast\PhpDoc\AssertTagValueNode($type, $parameter['parameter'], $isNegated, $description);
456467
}
457468

458-
private function parseAssertParameter(TokenIterator $tokens): string
469+
/**
470+
* @return array{parameter: string}|array{parameter: string, property: string}|array{parameter: string, method: string}
471+
*/
472+
private function parseAssertParameter(TokenIterator $tokens): array
459473
{
460474
if ($tokens->isCurrentTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
461475
$parameter = '$this';
@@ -470,17 +484,19 @@ private function parseAssertParameter(TokenIterator $tokens): string
470484
if ($requirePropertyOrMethod || $tokens->isCurrentTokenType(Lexer::TOKEN_ARROW)) {
471485
$tokens->consumeTokenType(Lexer::TOKEN_ARROW);
472486

473-
$parameter .= '->' . $tokens->currentTokenValue();
487+
$propertyOrMethod = $tokens->currentTokenValue();
474488
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
475489

476490
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
477491
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
478492

479-
$parameter .= '()';
493+
return ['parameter' => $parameter, 'method' => $propertyOrMethod];
480494
}
495+
496+
return ['parameter' => $parameter, 'property' => $propertyOrMethod];
481497
}
482498

483-
return $parameter;
499+
return ['parameter' => $parameter];
484500
}
485501

486502
private function parseOptionalVariableName(TokenIterator $tokens): string

tests/PHPStan/Parser/PhpDocParserTest.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
99
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
1010
use PHPStan\PhpDocParser\Ast\Node;
11+
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagMethodValueNode;
12+
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagPropertyValueNode;
1113
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagValueNode;
1214
use PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode;
1315
use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode;
@@ -3817,9 +3819,10 @@ public function provideAssertTagsData(): Iterator
38173819
new PhpDocNode([
38183820
new PhpDocTagNode(
38193821
'@phpstan-assert',
3820-
new AssertTagValueNode(
3822+
new AssertTagMethodValueNode(
38213823
new IdentifierTypeNode('Type'),
3822-
'$var->method()',
3824+
'$var',
3825+
'method',
38233826
false,
38243827
''
38253828
)
@@ -3833,9 +3836,10 @@ public function provideAssertTagsData(): Iterator
38333836
new PhpDocNode([
38343837
new PhpDocTagNode(
38353838
'@phpstan-assert',
3836-
new AssertTagValueNode(
3839+
new AssertTagPropertyValueNode(
38373840
new IdentifierTypeNode('Type'),
3838-
'$var->property',
3841+
'$var',
3842+
'property',
38393843
false,
38403844
''
38413845
)
@@ -3868,9 +3872,10 @@ public function provideAssertTagsData(): Iterator
38683872
new PhpDocNode([
38693873
new PhpDocTagNode(
38703874
'@phpstan-assert',
3871-
new AssertTagValueNode(
3875+
new AssertTagMethodValueNode(
38723876
new IdentifierTypeNode('Type'),
3873-
'$this->method()',
3877+
'$this',
3878+
'method',
38743879
false,
38753880
''
38763881
)
@@ -3884,9 +3889,10 @@ public function provideAssertTagsData(): Iterator
38843889
new PhpDocNode([
38853890
new PhpDocTagNode(
38863891
'@phpstan-assert',
3887-
new AssertTagValueNode(
3892+
new AssertTagPropertyValueNode(
38883893
new IdentifierTypeNode('Type'),
3889-
'$this->property',
3894+
'$this',
3895+
'property',
38903896
false,
38913897
''
38923898
)

0 commit comments

Comments
 (0)