Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
P
php-phpstan-phpdoc-parser
Manage
Activity
Members
Labels
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Debian PHP Team
PEAR
php-phpstan-phpdoc-parser
Commits
4cb3021a
Commit
4cb3021a
authored
Mar 30, 2022
by
Richard van Velzen
Committed by
Ondřej Mirtes
Mar 30, 2022
Browse files
Options
Downloads
Patches
Plain Diff
Support multiline parenthesized types
This includes union, intersection, and conditional types
parent
05333065
Branches
upstream
Branches containing commit
Tags
upstream/1.12.0
Tags containing commit
No related merge requests found
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
src/Parser/TypeParser.php
+50
-9
50 additions, 9 deletions
src/Parser/TypeParser.php
tests/PHPStan/Parser/PhpDocParserTest.php
+113
-0
113 additions, 0 deletions
tests/PHPStan/Parser/PhpDocParserTest.php
tests/PHPStan/Parser/TypeParserTest.php
+74
-0
74 additions, 0 deletions
tests/PHPStan/Parser/TypeParserTest.php
with
237 additions
and
9 deletions
src/Parser/TypeParser.php
+
50
−
9
View file @
4cb3021a
...
@@ -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
->
p
arseUnion
(
$tokens
,
$type
);
$type
=
$this
->
subP
arseUnion
(
$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
->
parse
Atomic
(
$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
->
parse
Atomic
(
$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
->
parse
Atomic
(
$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
);
}
}
...
...
This diff is collapsed.
Click to expand it.
tests/PHPStan/Parser/PhpDocParserTest.php
+
113
−
0
View file @
4cb3021a
...
@@ -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
),
''
)
),
]),
],
];
];
}
}
...
...
This diff is collapsed.
Click to expand it.
tests/PHPStan/Parser/TypeParserTest.php
+
74
−
0
View file @
4cb3021a
...
@@ -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
),
],
];
];
}
}
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment