1616use Microsoft \PhpParser \Node \ClassInterfaceClause ;
1717use Microsoft \PhpParser \Node \ClassMembersNode ;
1818use Microsoft \PhpParser \Node \ConstElement ;
19+ use Microsoft \PhpParser \Node \EnumCaseDeclaration ;
20+ use Microsoft \PhpParser \Node \EnumMembers ;
1921use Microsoft \PhpParser \Node \Expression ;
2022use Microsoft \PhpParser \Node \Expression \{
2123 AnonymousFunctionCreationExpression ,
8890 DeclareStatement ,
8991 DoStatement ,
9092 EmptyStatement ,
93+ EnumDeclaration ,
9194 ExpressionStatement ,
9295 ForeachStatement ,
9396 ForStatement ,
@@ -292,6 +295,7 @@ private function isListTerminator(int $parseContext) {
292295 case ParseContext::ClassMembers:
293296 case ParseContext::BlockStatements:
294297 case ParseContext::TraitMembers:
298+ case ParseContext::EnumMembers:
295299 return $ tokenKind === TokenKind::CloseBraceToken;
296300 case ParseContext::SwitchStatementElements:
297301 return $ tokenKind === TokenKind::CloseBraceToken || $ tokenKind === TokenKind::EndSwitchKeyword;
@@ -343,6 +347,9 @@ private function isValidListElement($context, Token $token) {
343347 case ParseContext::TraitMembers:
344348 return $ this ->isTraitMemberDeclarationStart ($ token );
345349
350+ case ParseContext::EnumMembers:
351+ return $ this ->isEnumMemberDeclarationStart ($ token );
352+
346353 case ParseContext::InterfaceMembers:
347354 return $ this ->isInterfaceMemberDeclarationStart ($ token );
348355
@@ -374,6 +381,9 @@ private function getParseListElementFn($context) {
374381 case ParseContext::InterfaceMembers:
375382 return $ this ->parseInterfaceElementFn ();
376383
384+ case ParseContext::EnumMembers:
385+ return $ this ->parseEnumElementFn ();
386+
377387 case ParseContext::SwitchStatementElements:
378388 return $ this ->parseCaseOrDefaultStatement ();
379389 default :
@@ -583,6 +593,9 @@ private function parseStatementFn() {
583593 case TokenKind::TraitKeyword:
584594 return $ this ->parseTraitDeclaration ($ parentNode );
585595
596+ case TokenKind::EnumKeyword:
597+ return $ this ->parseEnumDeclaration ($ parentNode );
598+
586599 // global-declaration
587600 case TokenKind::GlobalKeyword:
588601 return $ this ->parseGlobalDeclaration ($ parentNode );
@@ -715,12 +728,15 @@ private function parseAttributeStatement($parentNode) {
715728 } elseif ($ parentNode instanceof TraitMembers) {
716729 // Create a trait element or a MissingMemberDeclaration
717730 $ statement = $ this ->parseTraitElementFn ()($ parentNode );
731+ } elseif ($ parentNode instanceof EnumMembers) {
732+ // Create a enum element or a MissingMemberDeclaration
733+ $ statement = $ this ->parseEnumElementFn ()($ parentNode );
718734 } elseif ($ parentNode instanceof InterfaceMembers) {
719735 // Create an interface element or a MissingMemberDeclaration
720736 $ statement = $ this ->parseInterfaceElementFn ()($ parentNode );
721737 } else {
722738 // Classlikes, anonymous functions, global functions, and arrow functions can have attributes. Global constants cannot.
723- if (in_array ($ this ->token ->kind , [TokenKind::ClassKeyword, TokenKind::TraitKeyword, TokenKind::InterfaceKeyword, TokenKind::AbstractKeyword, TokenKind::FinalKeyword, TokenKind::FunctionKeyword, TokenKind::FnKeyword], true ) ||
739+ if (in_array ($ this ->token ->kind , [TokenKind::ClassKeyword, TokenKind::TraitKeyword, TokenKind::InterfaceKeyword, TokenKind::AbstractKeyword, TokenKind::FinalKeyword, TokenKind::FunctionKeyword, TokenKind::FnKeyword, TokenKind::EnumKeyword ], true ) ||
724740 $ this ->token ->kind === TokenKind::StaticKeyword && $ this ->lookahead ([TokenKind::FunctionKeyword, TokenKind::FnKeyword])) {
725741 $ statement = $ this ->parseStatement ($ parentNode );
726742 } else {
@@ -734,6 +750,8 @@ private function parseAttributeStatement($parentNode) {
734750 if ($ statement instanceof FunctionLike ||
735751 $ statement instanceof ClassDeclaration ||
736752 $ statement instanceof TraitDeclaration ||
753+ $ statement instanceof EnumDeclaration ||
754+ $ statement instanceof EnumCaseDeclaration ||
737755 $ statement instanceof InterfaceDeclaration ||
738756 $ statement instanceof ClassConstDeclaration ||
739757 $ statement instanceof PropertyDeclaration ||
@@ -1012,6 +1030,9 @@ private function isStatementStart(Token $token) {
10121030 // trait-declaration
10131031 case TokenKind::TraitKeyword:
10141032
1033+ // enum-declaration
1034+ case TokenKind::EnumKeyword:
1035+
10151036 // namespace-definition
10161037 case TokenKind::NamespaceKeyword:
10171038
@@ -3198,6 +3219,21 @@ private function parseClassConstDeclaration($parentNode, $modifiers) {
31983219 return $ classConstDeclaration ;
31993220 }
32003221
3222+ private function parseEnumCaseDeclaration ($ parentNode ) {
3223+ $ classConstDeclaration = new EnumCaseDeclaration ();
3224+ $ classConstDeclaration ->parent = $ parentNode ;
3225+ $ classConstDeclaration ->caseKeyword = $ this ->eat1 (TokenKind::CaseKeyword);
3226+ $ classConstDeclaration ->name = $ this ->eat ($ this ->nameOrKeywordOrReservedWordTokens );
3227+ $ classConstDeclaration ->equalsToken = $ this ->eatOptional1 (TokenKind::EqualsToken);
3228+ if ($ classConstDeclaration ->equalsToken !== null ) {
3229+ // TODO add post-parse rule that checks for invalid assignments
3230+ $ classConstDeclaration ->assignment = $ this ->parseExpression ($ classConstDeclaration );
3231+ }
3232+ $ classConstDeclaration ->semicolon = $ this ->eat1 (TokenKind::SemicolonToken);
3233+
3234+ return $ classConstDeclaration ;
3235+ }
3236+
32013237 /**
32023238 * @param Node $parentNode
32033239 * @param Token[] $modifiers
@@ -3520,6 +3556,102 @@ private function parseTraitElementFn() {
35203556 };
35213557 }
35223558
3559+ private function parseEnumDeclaration ($ parentNode ) {
3560+ $ enumDeclaration = new EnumDeclaration ();
3561+ $ enumDeclaration ->parent = $ parentNode ;
3562+
3563+ $ enumDeclaration ->enumKeyword = $ this ->eat1 (TokenKind::EnumKeyword);
3564+ $ enumDeclaration ->name = $ this ->eat1 (TokenKind::Name);
3565+ $ enumDeclaration ->colonToken = $ this ->eatOptional1 (TokenKind::ColonToken);
3566+ if ($ enumDeclaration ->colonToken !== null ) {
3567+ $ enumDeclaration ->enumType = $ this ->tryParseParameterTypeDeclaration ($ enumDeclaration )
3568+ ?: new MissingToken (TokenKind::EnumType, $ this ->token ->fullStart );
3569+ }
3570+
3571+ $ enumDeclaration ->enumMembers = $ this ->parseEnumMembers ($ enumDeclaration );
3572+
3573+ return $ enumDeclaration ;
3574+ }
3575+
3576+ private function parseEnumMembers ($ parentNode ) {
3577+ $ enumMembers = new EnumMembers ();
3578+ $ enumMembers ->parent = $ parentNode ;
3579+
3580+ $ enumMembers ->openBrace = $ this ->eat1 (TokenKind::OpenBraceToken);
3581+
3582+ $ enumMembers ->enumMemberDeclarations = $ this ->parseList ($ enumMembers , ParseContext::EnumMembers);
3583+
3584+ $ enumMembers ->closeBrace = $ this ->eat1 (TokenKind::CloseBraceToken);
3585+
3586+ return $ enumMembers ;
3587+ }
3588+
3589+ private function isEnumMemberDeclarationStart ($ token ) {
3590+ switch ($ token ->kind ) {
3591+ // modifiers
3592+ case TokenKind::PublicKeyword:
3593+ case TokenKind::ProtectedKeyword:
3594+ case TokenKind::PrivateKeyword:
3595+ // case TokenKind::VarKeyword:
3596+ case TokenKind::StaticKeyword:
3597+ case TokenKind::AbstractKeyword:
3598+ case TokenKind::FinalKeyword:
3599+
3600+ // method-declaration
3601+ case TokenKind::FunctionKeyword:
3602+
3603+ // trait-use-clauses (enums can use traits)
3604+ case TokenKind::UseKeyword:
3605+
3606+ // cases and constants
3607+ case TokenKind::CaseKeyword:
3608+ case TokenKind::ConstKeyword:
3609+
3610+ // attributes
3611+ case TokenKind::AttributeToken:
3612+ return true ;
3613+ }
3614+ return false ;
3615+ }
3616+
3617+ private function parseEnumElementFn () {
3618+ return function ($ parentNode ) {
3619+ $ modifiers = $ this ->parseModifiers ();
3620+
3621+ $ token = $ this ->getCurrentToken ();
3622+ switch ($ token ->kind ) {
3623+ // TODO: CaseKeyword
3624+ case TokenKind::CaseKeyword:
3625+ return $ this ->parseEnumCaseDeclaration ($ parentNode );
3626+
3627+ case TokenKind::ConstKeyword:
3628+ return $ this ->parseClassConstDeclaration ($ parentNode , $ modifiers );
3629+
3630+ case TokenKind::FunctionKeyword:
3631+ return $ this ->parseMethodDeclaration ($ parentNode , $ modifiers );
3632+
3633+ case TokenKind::QuestionToken:
3634+ return $ this ->parseRemainingPropertyDeclarationOrMissingMemberDeclaration (
3635+ $ parentNode ,
3636+ $ modifiers ,
3637+ $ this ->eat1 (TokenKind::QuestionToken)
3638+ );
3639+ case TokenKind::VariableName:
3640+ return $ this ->parsePropertyDeclaration ($ parentNode , $ modifiers );
3641+
3642+ case TokenKind::UseKeyword:
3643+ return $ this ->parseTraitUseClause ($ parentNode );
3644+
3645+ case TokenKind::AttributeToken:
3646+ return $ this ->parseAttributeStatement ($ parentNode );
3647+
3648+ default :
3649+ return $ this ->parseRemainingPropertyDeclarationOrMissingMemberDeclaration ($ parentNode , $ modifiers );
3650+ }
3651+ };
3652+ }
3653+
3654+
35233655 /**
35243656 * @param Node $parentNode
35253657 * @param Token[] $modifiers
0 commit comments