Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4cb3021

Browse files
rvanvelzenondrejmirtes
authored andcommitted
Support multiline parenthesized types
This includes union, intersection, and conditional types
1 parent 0533306 commit 4cb3021

File tree

3 files changed

+237
-9
lines changed

3 files changed

+237
-9
lines changed

‎src/Parser/TypeParser.php‎

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,17 @@ private function subParse(TokenIterator $tokens): Ast\Type\TypeNode
5151
} else {
5252
$type = $this->parseAtomic($tokens);
5353

54-
if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
55-
$type = $this->parseUnion($tokens, $type);
56-
57-
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
58-
$type = $this->parseIntersection($tokens, $type);
59-
} elseif ($tokens->isCurrentTokenValue('is')) {
54+
if ($tokens->isCurrentTokenValue('is')) {
6055
$type = $this->parseConditional($tokens, $type);
56+
} else {
57+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
58+
59+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
60+
$type = $this->subParseUnion($tokens, $type);
61+
62+
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
63+
$type = $this->subParseIntersection($tokens, $type);
64+
}
6165
}
6266
}
6367

@@ -69,7 +73,10 @@ private function subParse(TokenIterator $tokens): Ast\Type\TypeNode
6973
private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
7074
{
7175
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
76+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
7277
$type = $this->subParse($tokens);
78+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
79+
7380
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
7481

7582
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
@@ -169,6 +176,21 @@ private function parseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast
169176
}
170177

171178

179+
/** @phpstan-impure */
180+
private function subParseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
181+
{
182+
$types = [$type];
183+
184+
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
185+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
186+
$types[] = $this->parseAtomic($tokens);
187+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
188+
}
189+
190+
return new Ast\Type\UnionTypeNode($types);
191+
}
192+
193+
172194
/** @phpstan-impure */
173195
private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
174196
{
@@ -182,6 +204,21 @@ private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $typ
182204
}
183205

184206

207+
/** @phpstan-impure */
208+
private function subParseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
209+
{
210+
$types = [$type];
211+
212+
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
213+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
214+
$types[] = $this->parseAtomic($tokens);
215+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
216+
}
217+
218+
return new Ast\Type\IntersectionTypeNode($types);
219+
}
220+
221+
185222
/** @phpstan-impure */
186223
private function parseConditional(TokenIterator $tokens, Ast\Type\TypeNode $subjectType): Ast\Type\TypeNode
187224
{
@@ -193,15 +230,19 @@ private function parseConditional(TokenIterator $tokens, Ast\Type\TypeNode $subj
193230
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
194231
}
195232

196-
$targetType = $this->parseAtomic($tokens);
233+
$targetType = $this->parse($tokens);
197234

235+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
198236
$tokens->consumeTokenType(Lexer::TOKEN_NULLABLE);
237+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
199238

200-
$ifType = $this->parseAtomic($tokens);
239+
$ifType = $this->parse($tokens);
201240

241+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
202242
$tokens->consumeTokenType(Lexer::TOKEN_COLON);
243+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
203244

204-
$elseType = $this->parseAtomic($tokens);
245+
$elseType = $this->parse($tokens);
205246

206247
return new Ast\Type\ConditionalTypeNode($subjectType, $targetType, $ifType, $elseType, $negated);
207248
}

‎tests/PHPStan/Parser/PhpDocParserTest.php‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2818,6 +2818,119 @@ public function provideMultiLinePhpDocData(): array
28182818
),
28192819
]),
28202820
],
2821+
[
2822+
'OK with multiline conditional return type',
2823+
'/**
2824+
* @template TRandKey as array-key
2825+
* @template TRandVal
2826+
* @template TRandList as array<TRandKey, TRandVal>|XIterator<TRandKey, TRandVal>|Traversable<TRandKey, TRandVal>
2827+
*
2828+
* @param TRandList $list
2829+
*
2830+
* @return (
2831+
* TRandList is array ? array<TRandKey, TRandVal> : (
2832+
* TRandList is XIterator ? XIterator<TRandKey, TRandVal> :
2833+
* IteratorIterator<TRandKey, TRandVal>|LimitIterator<TRandKey, TRandVal>
2834+
* ))
2835+
*/',
2836+
new PhpDocNode([
2837+
new PhpDocTagNode(
2838+
'@template',
2839+
new TemplateTagValueNode('TRandKey', new IdentifierTypeNode('array-key'), '')
2840+
),
2841+
new PhpDocTagNode(
2842+
'@template',
2843+
new TemplateTagValueNode('TRandVal', null, '')
2844+
),
2845+
new PhpDocTagNode(
2846+
'@template',
2847+
new TemplateTagValueNode(
2848+
'TRandList',
2849+
new UnionTypeNode([
2850+
new GenericTypeNode(
2851+
new IdentifierTypeNode('array'),
2852+
[
2853+
new IdentifierTypeNode('TRandKey'),
2854+
new IdentifierTypeNode('TRandVal'),
2855+
]
2856+
),
2857+
new GenericTypeNode(
2858+
new IdentifierTypeNode('XIterator'),
2859+
[
2860+
new IdentifierTypeNode('TRandKey'),
2861+
new IdentifierTypeNode('TRandVal'),
2862+
]
2863+
),
2864+
new GenericTypeNode(
2865+
new IdentifierTypeNode('Traversable'),
2866+
[
2867+
new IdentifierTypeNode('TRandKey'),
2868+
new IdentifierTypeNode('TRandVal'),
2869+
]
2870+
),
2871+
]),
2872+
''
2873+
)
2874+
),
2875+
new PhpDocTextNode(''),
2876+
new PhpDocTagNode(
2877+
'@param',
2878+
new ParamTagValueNode(
2879+
new IdentifierTypeNode('TRandList'),
2880+
false,
2881+
'$list',
2882+
''
2883+
)
2884+
),
2885+
new PhpDocTextNode(''),
2886+
new PhpDocTagNode(
2887+
'@return',
2888+
new ReturnTagValueNode(
2889+
new ConditionalTypeNode(
2890+
new IdentifierTypeNode('TRandList'),
2891+
new IdentifierTypeNode('array'),
2892+
new GenericTypeNode(
2893+
new IdentifierTypeNode('array'),
2894+
[
2895+
new IdentifierTypeNode('TRandKey'),
2896+
new IdentifierTypeNode('TRandVal'),
2897+
]
2898+
),
2899+
new ConditionalTypeNode(
2900+
new IdentifierTypeNode('TRandList'),
2901+
new IdentifierTypeNode('XIterator'),
2902+
new GenericTypeNode(
2903+
new IdentifierTypeNode('XIterator'),
2904+
[
2905+
new IdentifierTypeNode('TRandKey'),
2906+
new IdentifierTypeNode('TRandVal'),
2907+
]
2908+
),
2909+
new UnionTypeNode([
2910+
new GenericTypeNode(
2911+
new IdentifierTypeNode('IteratorIterator'),
2912+
[
2913+
new IdentifierTypeNode('TRandKey'),
2914+
new IdentifierTypeNode('TRandVal'),
2915+
]
2916+
),
2917+
new GenericTypeNode(
2918+
new IdentifierTypeNode('LimitIterator'),
2919+
[
2920+
new IdentifierTypeNode('TRandKey'),
2921+
new IdentifierTypeNode('TRandVal'),
2922+
]
2923+
),
2924+
]),
2925+
false
2926+
),
2927+
false
2928+
),
2929+
''
2930+
)
2931+
),
2932+
]),
2933+
],
28212934
];
28222935
}
28232936

‎tests/PHPStan/Parser/TypeParserTest.php‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,17 @@ public function provideParseData(): array
149149
new IdentifierTypeNode('int'),
150150
]),
151151
],
152+
[
153+
'(' . PHP_EOL .
154+
' string' . PHP_EOL .
155+
' &' . PHP_EOL .
156+
' int' . PHP_EOL .
157+
')',
158+
new IntersectionTypeNode([
159+
new IdentifierTypeNode('string'),
160+
new IdentifierTypeNode('int'),
161+
]),
162+
],
152163
[
153164
'string & int & float',
154165
new IntersectionTypeNode([
@@ -1136,6 +1147,69 @@ public function provideParseData(): array
11361147
false
11371148
),
11381149
],
1150+
[
1151+
'(Foo is Bar|Baz ? never : int|string)',
1152+
new ConditionalTypeNode(
1153+
new IdentifierTypeNode('Foo'),
1154+
new UnionTypeNode([
1155+
new IdentifierTypeNode('Bar'),
1156+
new IdentifierTypeNode('Baz'),
1157+
]),
1158+
new IdentifierTypeNode('never'),
1159+
new UnionTypeNode([
1160+
new IdentifierTypeNode('int'),
1161+
new IdentifierTypeNode('string'),
1162+
]),
1163+
false
1164+
),
1165+
],
1166+
[
1167+
'(' . PHP_EOL .
1168+
' TRandList is array ? array<TRandKey, TRandVal> : (' . PHP_EOL .
1169+
' TRandList is XIterator ? XIterator<TRandKey, TRandVal> :' . PHP_EOL .
1170+
' IteratorIterator<TRandKey, TRandVal>|LimitIterator<TRandKey, TRandVal>' . PHP_EOL .
1171+
'))',
1172+
new ConditionalTypeNode(
1173+
new IdentifierTypeNode('TRandList'),
1174+
new IdentifierTypeNode('array'),
1175+
new GenericTypeNode(
1176+
new IdentifierTypeNode('array'),
1177+
[
1178+
new IdentifierTypeNode('TRandKey'),
1179+
new IdentifierTypeNode('TRandVal'),
1180+
]
1181+
),
1182+
new ConditionalTypeNode(
1183+
new IdentifierTypeNode('TRandList'),
1184+
new IdentifierTypeNode('XIterator'),
1185+
new GenericTypeNode(
1186+
new IdentifierTypeNode('XIterator'),
1187+
[
1188+
new IdentifierTypeNode('TRandKey'),
1189+
new IdentifierTypeNode('TRandVal'),
1190+
]
1191+
),
1192+
new UnionTypeNode([
1193+
new GenericTypeNode(
1194+
new IdentifierTypeNode('IteratorIterator'),
1195+
[
1196+
new IdentifierTypeNode('TRandKey'),
1197+
new IdentifierTypeNode('TRandVal'),
1198+
]
1199+
),
1200+
new GenericTypeNode(
1201+
new IdentifierTypeNode('LimitIterator'),
1202+
[
1203+
new IdentifierTypeNode('TRandKey'),
1204+
new IdentifierTypeNode('TRandVal'),
1205+
]
1206+
),
1207+
]),
1208+
false
1209+
),
1210+
false
1211+
),
1212+
],
11391213
];
11401214
}
11411215

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /