6
6
7
7
namespace Microsoft \PhpParser ;
8
8
9
+ use Closure ;
9
10
use Microsoft \PhpParser \Node \AnonymousFunctionUseClause ;
10
11
use Microsoft \PhpParser \Node \ArrayElement ;
11
12
use Microsoft \PhpParser \Node \Attribute ;
70
71
use Microsoft \PhpParser \Node \NamespaceAliasingClause ;
71
72
use Microsoft \PhpParser \Node \NamespaceUseGroupClause ;
72
73
use Microsoft \PhpParser \Node \NumericLiteral ;
74
+ use Microsoft \PhpParser \Node \ParenthesizedIntersectionType ;
73
75
use Microsoft \PhpParser \Node \PropertyDeclaration ;
74
76
use Microsoft \PhpParser \Node \ReservedWord ;
75
77
use Microsoft \PhpParser \Node \StringLiteral ;
@@ -894,13 +896,31 @@ private function parseAndSetReturnTypeDeclarationList($parentNode) {
894
896
* @return DelimitedList\QualifiedNameList|null
895
897
*/
896
898
private function parseReturnTypeDeclarationList ($ parentNode ) {
899
+ // TODO: Forbid mixing `|` and `&` in another PR, that's a parse error.
900
+ // TODO: Forbid mixing (A&B)&C in another PR, that's a parse error.
897
901
$ result = $ this ->parseDelimitedList (
898
902
DelimitedList \QualifiedNameList::class,
899
903
self ::TYPE_DELIMITER_TOKENS ,
900
904
function ($ token ) {
901
- return \in_array ($ token ->kind , $ this ->returnTypeDeclarationTokens , true ) || $ this ->isQualifiedNameStart ($ token );
905
+ return \in_array ($ token ->kind , $ this ->returnTypeDeclarationTokens , true ) ||
906
+ $ token ->kind === TokenKind::OpenParenToken ||
907
+ $ this ->isQualifiedNameStart ($ token );
902
908
},
903
909
function ($ parentNode ) {
910
+ $ openParen = $ this ->eatOptional (TokenKind::OpenParenToken);
911
+ if ($ openParen ) {
912
+ return $ this ->parseParenthesizedIntersectionType (
913
+ $ parentNode ,
914
+ $ openParen ,
915
+ function ($ token ) {
916
+ return \in_array ($ token ->kind , $ this ->returnTypeDeclarationTokens , true ) ||
917
+ $ this ->isQualifiedNameStart ($ token );
918
+ },
919
+ function ($ parentNode ) {
920
+ return $ this ->parseReturnTypeDeclaration ($ parentNode );
921
+ }
922
+ );
923
+ }
904
924
return $ this ->parseReturnTypeDeclaration ($ parentNode );
905
925
},
906
926
$ parentNode ,
@@ -925,18 +945,62 @@ private function tryParseParameterTypeDeclaration($parentNode) {
925
945
return $ parameterTypeDeclaration ;
926
946
}
927
947
948
+ private function parseParenthesizedIntersectionType ($ parentNode , Token $ openParen , Closure $ isTypeStart , Closure $ parseType ): ParenthesizedIntersectionType {
949
+ $ node = new ParenthesizedIntersectionType ();
950
+ $ node ->parent = $ parentNode ;
951
+ $ node ->openParen = $ openParen ;
952
+ $ node ->children = $ this ->parseDelimitedList (
953
+ DelimitedList \QualifiedNameList::class,
954
+ TokenKind::AmpersandToken,
955
+ $ isTypeStart ,
956
+ $ parseType ,
957
+ $ node ,
958
+ true );
959
+ if ($ node ->children ) {
960
+ // https://wiki.php.net/rfc/dnf_types
961
+ if ((end ($ node ->children ->children )->kind ?? null ) === TokenKind::OpenParenToken) {
962
+ // Add a MissingToken so that this will Warn about `function (A|(B&) $x) {}`
963
+ $ node ->children ->children [] = new MissingToken (TokenKind::Name, $ this ->token ->fullStart );
964
+ } elseif (count ($ node ->children ->children ) === 1 ) {
965
+ // Must have at least 2 parts for A|(B&C)
966
+ $ node ->children ->children [] = new MissingToken (TokenKind::AmpersandToken, $ this ->token ->fullStart );
967
+ }
968
+ } else {
969
+ // Having less than 2 types (no types) in A|() is a parse error
970
+ $ node ->children = new MissingToken (TokenKind::Name, $ this ->token ->fullStart );
971
+ }
972
+ $ node ->closeParen = $ this ->eat (TokenKind::CloseParenToken);
973
+ return $ node ;
974
+ }
975
+
928
976
/**
929
- * @param Node $parentNode
977
+ * @param Node|null $parentNode
930
978
* @return DelimitedList\QualifiedNameList|null
931
979
*/
932
980
private function tryParseParameterTypeDeclarationList ($ parentNode ) {
933
981
$ result = $ this ->parseDelimitedList (
934
982
DelimitedList \QualifiedNameList::class,
935
983
self ::TYPE_DELIMITER_TOKENS ,
936
984
function ($ token ) {
937
- return \in_array ($ token ->kind , $ this ->parameterTypeDeclarationTokens , true ) || $ this ->isQualifiedNameStart ($ token );
985
+ return \in_array ($ token ->kind , $ this ->parameterTypeDeclarationTokens , true ) ||
986
+ $ token ->kind === TokenKind::OpenParenToken ||
987
+ $ this ->isQualifiedNameStart ($ token );
938
988
},
939
989
function ($ parentNode ) {
990
+ $ openParen = $ this ->eatOptional (TokenKind::OpenParenToken);
991
+ if ($ openParen ) {
992
+ return $ this ->parseParenthesizedIntersectionType (
993
+ $ parentNode ,
994
+ $ openParen ,
995
+ function ($ token ) {
996
+ return \in_array ($ token ->kind , $ this ->parameterTypeDeclarationTokens , true ) ||
997
+ $ this ->isQualifiedNameStart ($ token );
998
+ },
999
+ function ($ parentNode ) {
1000
+ return $ this ->tryParseParameterTypeDeclaration ($ parentNode );
1001
+ }
1002
+ );
1003
+ }
940
1004
return $ this ->tryParseParameterTypeDeclaration ($ parentNode );
941
1005
},
942
1006
$ parentNode ,
@@ -959,12 +1023,6 @@ private function parseCompoundStatement($parentNode) {
959
1023
return $ compoundStatement ;
960
1024
}
961
1025
962
- private function array_push_list (& $ array , $ list ) {
963
- foreach ($ list as $ item ) {
964
- $ array [] = $ item ;
965
- }
966
- }
967
-
968
1026
private function isClassMemberDeclarationStart (Token $ token ) {
969
1027
switch ($ token ->kind ) {
970
1028
// const-modifier
@@ -1544,6 +1602,9 @@ private function isParameterStartFn() {
1544
1602
case TokenKind::ProtectedKeyword:
1545
1603
case TokenKind::PrivateKeyword:
1546
1604
case TokenKind::AttributeToken:
1605
+
1606
+ // dnf types (A&B)|C
1607
+ case TokenKind::OpenParenToken:
1547
1608
return true ;
1548
1609
}
1549
1610
@@ -3306,6 +3367,8 @@ private function parsePropertyDeclaration($parentNode, $modifiers, $questionToke
3306
3367
}
3307
3368
3308
3369
/**
3370
+ * Parse a comma separated qualified name list (e.g. interfaces implemented by a class)
3371
+ *
3309
3372
* @param Node $parentNode
3310
3373
* @return DelimitedList\QualifiedNameList
3311
3374
*/
@@ -3319,6 +3382,7 @@ private function parseQualifiedNameList($parentNode) {
3319
3382
}
3320
3383
3321
3384
private function parseQualifiedNameCatchList ($ parentNode ) {
3385
+ // catch blocks don't support intersection types.
3322
3386
$ result = $ this ->parseDelimitedList (
3323
3387
DelimitedList \QualifiedNameList::class,
3324
3388
TokenKind::BarToken,
0 commit comments