Skip to content

Commit 956e721

Browse files
committed
Support for parsing @mixin Type [description]
1 parent fde6cca commit 956e721

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

Diff for: src/Ast/PhpDoc/MixinTagValueNode.php

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

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

+14
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,20 @@ public function getThrowsTagValues(string $tagName = '@throws'): array
154154
}
155155

156156

157+
/**
158+
* @return MixinTagValueNode[]
159+
*/
160+
public function getMixinTagValues(string $tagName = '@mixin'): array
161+
{
162+
return array_column(
163+
array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool {
164+
return $tag->value instanceof MixinTagValueNode;
165+
}),
166+
'value'
167+
);
168+
}
169+
170+
157171
/**
158172
* @return \PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode[]
159173
*/

Diff for: src/Parser/PhpDocParser.php

+11
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
136136
$tagValue = $this->parseThrowsTagValue($tokens);
137137
break;
138138

139+
case '@mixin':
140+
$tagValue = $this->parseMixinTagValue($tokens);
141+
break;
142+
139143
case '@deprecated':
140144
$tagValue = $this->parseDeprecatedTagValue($tokens);
141145
break;
@@ -235,6 +239,13 @@ private function parseThrowsTagValue(TokenIterator $tokens): Ast\PhpDoc\ThrowsTa
235239
return new Ast\PhpDoc\ThrowsTagValueNode($type, $description);
236240
}
237241

242+
private function parseMixinTagValue(TokenIterator $tokens): Ast\PhpDoc\MixinTagValueNode
243+
{
244+
$type = $this->typeParser->parse($tokens);
245+
$description = $this->parseOptionalDescription($tokens, true);
246+
return new Ast\PhpDoc\MixinTagValueNode($type, $description);
247+
}
248+
238249
private function parseDeprecatedTagValue(TokenIterator $tokens): Ast\PhpDoc\DeprecatedTagValueNode
239250
{
240251
$description = $this->parseOptionalDescription($tokens);

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

+101
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
1313
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
1414
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
15+
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
1516
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
1617
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
1718
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
@@ -54,6 +55,7 @@ protected function setUp(): void
5455
* @dataProvider provideVarTagsData
5556
* @dataProvider provideReturnTagsData
5657
* @dataProvider provideThrowsTagsData
58+
* @dataProvider provideMixinTagsData
5759
* @dataProvider provideDeprecatedTagsData
5860
* @dataProvider providePropertyTagsData
5961
* @dataProvider provideMethodTagsData
@@ -1064,6 +1066,105 @@ public function provideThrowsTagsData(): \Iterator
10641066
];
10651067
}
10661068

1069+
public function provideMixinTagsData(): \Iterator
1070+
{
1071+
yield [
1072+
'OK without description',
1073+
'/** @mixin Foo */',
1074+
new PhpDocNode([
1075+
new PhpDocTagNode(
1076+
'@mixin',
1077+
new MixinTagValueNode(
1078+
new IdentifierTypeNode('Foo'),
1079+
''
1080+
)
1081+
),
1082+
]),
1083+
];
1084+
1085+
yield [
1086+
'OK with description',
1087+
'/** @mixin Foo optional description */',
1088+
new PhpDocNode([
1089+
new PhpDocTagNode(
1090+
'@mixin',
1091+
new MixinTagValueNode(
1092+
new IdentifierTypeNode('Foo'),
1093+
'optional description'
1094+
)
1095+
),
1096+
]),
1097+
];
1098+
1099+
yield [
1100+
'OK with description that starts with TOKEN_OPEN_SQUARE_BRACKET',
1101+
'/** @mixin Foo [Bar] */',
1102+
new PhpDocNode([
1103+
new PhpDocTagNode(
1104+
'@mixin',
1105+
new MixinTagValueNode(
1106+
new IdentifierTypeNode('Foo'),
1107+
'[Bar]'
1108+
)
1109+
),
1110+
]),
1111+
];
1112+
1113+
yield [
1114+
'invalid without type and description',
1115+
'/** @mixin */',
1116+
new PhpDocNode([
1117+
new PhpDocTagNode(
1118+
'@mixin',
1119+
new InvalidTagValueNode(
1120+
'',
1121+
new \PHPStan\PhpDocParser\Parser\ParserException(
1122+
'*/',
1123+
Lexer::TOKEN_CLOSE_PHPDOC,
1124+
11,
1125+
Lexer::TOKEN_IDENTIFIER
1126+
)
1127+
)
1128+
),
1129+
]),
1130+
];
1131+
1132+
yield [
1133+
'invalid with type and disallowed description start token',
1134+
'/** @mixin Foo | #desc */',
1135+
new PhpDocNode([
1136+
new PhpDocTagNode(
1137+
'@mixin',
1138+
new InvalidTagValueNode(
1139+
'Foo | #desc',
1140+
new \PHPStan\PhpDocParser\Parser\ParserException(
1141+
'#desc',
1142+
Lexer::TOKEN_OTHER,
1143+
17,
1144+
Lexer::TOKEN_IDENTIFIER
1145+
)
1146+
)
1147+
),
1148+
]),
1149+
];
1150+
1151+
yield [
1152+
'generic @mixin',
1153+
'/** @mixin Foo<Bar> */',
1154+
new PhpDocNode([
1155+
new PhpDocTagNode(
1156+
'@mixin',
1157+
new MixinTagValueNode(
1158+
new GenericTypeNode(new IdentifierTypeNode('Foo'), [
1159+
new IdentifierTypeNode('Bar'),
1160+
]),
1161+
''
1162+
)
1163+
),
1164+
]),
1165+
];
1166+
}
1167+
10671168
public function provideDeprecatedTagsData(): \Iterator
10681169
{
10691170
yield [

0 commit comments

Comments
 (0)