Skip to content
Snippets Groups Projects
Commit 4cb3021a authored by Richard van Velzen's avatar Richard van Velzen Committed by Ondřej Mirtes
Browse files

Support multiline parenthesized types

This includes union, intersection, and conditional types
parent 05333065
Branches upstream
Tags upstream/1.12.0
No related merge requests found
...@@ -51,13 +51,17 @@ class TypeParser ...@@ -51,13 +51,17 @@ class TypeParser
} else { } else {
$type = $this->parseAtomic($tokens); $type = $this->parseAtomic($tokens);
if ($tokens->isCurrentTokenValue('is')) {
$type = $this->parseConditional($tokens, $type);
} else {
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) { if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
$type = $this->parseUnion($tokens, $type); $type = $this->subParseUnion($tokens, $type);
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) { } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
$type = $this->parseIntersection($tokens, $type); $type = $this->subParseIntersection($tokens, $type);
} elseif ($tokens->isCurrentTokenValue('is')) { }
$type = $this->parseConditional($tokens, $type);
} }
} }
...@@ -69,7 +73,10 @@ class TypeParser ...@@ -69,7 +73,10 @@ class TypeParser
private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
{ {
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$type = $this->subParse($tokens); $type = $this->subParse($tokens);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
...@@ -169,6 +176,21 @@ class TypeParser ...@@ -169,6 +176,21 @@ class TypeParser
} }
/** @phpstan-impure */
private function subParseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
{
$types = [$type];
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$types[] = $this->parseAtomic($tokens);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
}
return new Ast\Type\UnionTypeNode($types);
}
/** @phpstan-impure */ /** @phpstan-impure */
private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
{ {
...@@ -182,6 +204,21 @@ class TypeParser ...@@ -182,6 +204,21 @@ class TypeParser
} }
/** @phpstan-impure */
private function subParseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
{
$types = [$type];
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$types[] = $this->parseAtomic($tokens);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
}
return new Ast\Type\IntersectionTypeNode($types);
}
/** @phpstan-impure */ /** @phpstan-impure */
private function parseConditional(TokenIterator $tokens, Ast\Type\TypeNode $subjectType): Ast\Type\TypeNode private function parseConditional(TokenIterator $tokens, Ast\Type\TypeNode $subjectType): Ast\Type\TypeNode
{ {
...@@ -193,15 +230,19 @@ class TypeParser ...@@ -193,15 +230,19 @@ class TypeParser
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
} }
$targetType = $this->parseAtomic($tokens); $targetType = $this->parse($tokens);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$tokens->consumeTokenType(Lexer::TOKEN_NULLABLE); $tokens->consumeTokenType(Lexer::TOKEN_NULLABLE);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$ifType = $this->parseAtomic($tokens); $ifType = $this->parse($tokens);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$tokens->consumeTokenType(Lexer::TOKEN_COLON); $tokens->consumeTokenType(Lexer::TOKEN_COLON);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$elseType = $this->parseAtomic($tokens); $elseType = $this->parse($tokens);
return new Ast\Type\ConditionalTypeNode($subjectType, $targetType, $ifType, $elseType, $negated); return new Ast\Type\ConditionalTypeNode($subjectType, $targetType, $ifType, $elseType, $negated);
} }
......
...@@ -2818,6 +2818,119 @@ some text in the middle' ...@@ -2818,6 +2818,119 @@ some text in the middle'
), ),
]), ]),
], ],
[
'OK with multiline conditional return type',
'/**
* @template TRandKey as array-key
* @template TRandVal
* @template TRandList as array<TRandKey, TRandVal>|XIterator<TRandKey, TRandVal>|Traversable<TRandKey, TRandVal>
*
* @param TRandList $list
*
* @return (
* TRandList is array ? array<TRandKey, TRandVal> : (
* TRandList is XIterator ? XIterator<TRandKey, TRandVal> :
* IteratorIterator<TRandKey, TRandVal>|LimitIterator<TRandKey, TRandVal>
* ))
*/',
new PhpDocNode([
new PhpDocTagNode(
'@template',
new TemplateTagValueNode('TRandKey', new IdentifierTypeNode('array-key'), '')
),
new PhpDocTagNode(
'@template',
new TemplateTagValueNode('TRandVal', null, '')
),
new PhpDocTagNode(
'@template',
new TemplateTagValueNode(
'TRandList',
new UnionTypeNode([
new GenericTypeNode(
new IdentifierTypeNode('array'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
new GenericTypeNode(
new IdentifierTypeNode('XIterator'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
new GenericTypeNode(
new IdentifierTypeNode('Traversable'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
]),
''
)
),
new PhpDocTextNode(''),
new PhpDocTagNode(
'@param',
new ParamTagValueNode(
new IdentifierTypeNode('TRandList'),
false,
'$list',
''
)
),
new PhpDocTextNode(''),
new PhpDocTagNode(
'@return',
new ReturnTagValueNode(
new ConditionalTypeNode(
new IdentifierTypeNode('TRandList'),
new IdentifierTypeNode('array'),
new GenericTypeNode(
new IdentifierTypeNode('array'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
new ConditionalTypeNode(
new IdentifierTypeNode('TRandList'),
new IdentifierTypeNode('XIterator'),
new GenericTypeNode(
new IdentifierTypeNode('XIterator'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
new UnionTypeNode([
new GenericTypeNode(
new IdentifierTypeNode('IteratorIterator'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
new GenericTypeNode(
new IdentifierTypeNode('LimitIterator'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
]),
false
),
false
),
''
)
),
]),
],
]; ];
} }
......
...@@ -149,6 +149,17 @@ class TypeParserTest extends TestCase ...@@ -149,6 +149,17 @@ class TypeParserTest extends TestCase
new IdentifierTypeNode('int'), new IdentifierTypeNode('int'),
]), ]),
], ],
[
'(' . PHP_EOL .
' string' . PHP_EOL .
' &' . PHP_EOL .
' int' . PHP_EOL .
')',
new IntersectionTypeNode([
new IdentifierTypeNode('string'),
new IdentifierTypeNode('int'),
]),
],
[ [
'string & int & float', 'string & int & float',
new IntersectionTypeNode([ new IntersectionTypeNode([
...@@ -1136,6 +1147,69 @@ class TypeParserTest extends TestCase ...@@ -1136,6 +1147,69 @@ class TypeParserTest extends TestCase
false false
), ),
], ],
[
'(Foo is Bar|Baz ? never : int|string)',
new ConditionalTypeNode(
new IdentifierTypeNode('Foo'),
new UnionTypeNode([
new IdentifierTypeNode('Bar'),
new IdentifierTypeNode('Baz'),
]),
new IdentifierTypeNode('never'),
new UnionTypeNode([
new IdentifierTypeNode('int'),
new IdentifierTypeNode('string'),
]),
false
),
],
[
'(' . PHP_EOL .
' TRandList is array ? array<TRandKey, TRandVal> : (' . PHP_EOL .
' TRandList is XIterator ? XIterator<TRandKey, TRandVal> :' . PHP_EOL .
' IteratorIterator<TRandKey, TRandVal>|LimitIterator<TRandKey, TRandVal>' . PHP_EOL .
'))',
new ConditionalTypeNode(
new IdentifierTypeNode('TRandList'),
new IdentifierTypeNode('array'),
new GenericTypeNode(
new IdentifierTypeNode('array'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
new ConditionalTypeNode(
new IdentifierTypeNode('TRandList'),
new IdentifierTypeNode('XIterator'),
new GenericTypeNode(
new IdentifierTypeNode('XIterator'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
new UnionTypeNode([
new GenericTypeNode(
new IdentifierTypeNode('IteratorIterator'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
new GenericTypeNode(
new IdentifierTypeNode('LimitIterator'),
[
new IdentifierTypeNode('TRandKey'),
new IdentifierTypeNode('TRandVal'),
]
),
]),
false
),
false
),
],
]; ];
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment