Merge pull request #352 from TysonAndre/enum-support
Support parsing PHP 8.1 enums
This commit is contained in:
Коммит
35646d5501
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
namespace Microsoft\PhpParser\Node;
|
||||
|
||||
use Microsoft\PhpParser\Node;
|
||||
use Microsoft\PhpParser\Token;
|
||||
|
||||
class EnumCaseDeclaration extends Node {
|
||||
/** @var AttributeGroup[]|null */
|
||||
public $attributes;
|
||||
|
||||
/** @var Token */
|
||||
public $caseKeyword;
|
||||
|
||||
/** @var QualifiedName */
|
||||
public $name;
|
||||
|
||||
/** @var Token|null */
|
||||
public $equalsToken;
|
||||
|
||||
/** @var Token|Node|null */
|
||||
public $assignment;
|
||||
|
||||
/** @var Token */
|
||||
public $semicolon;
|
||||
|
||||
const CHILD_NAMES = [
|
||||
'attributes',
|
||||
'caseKeyword',
|
||||
'name',
|
||||
'equalsToken',
|
||||
'assignment',
|
||||
'semicolon',
|
||||
];
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
namespace Microsoft\PhpParser\Node;
|
||||
|
||||
use Microsoft\PhpParser\Node;
|
||||
use Microsoft\PhpParser\Token;
|
||||
|
||||
class EnumMembers extends Node {
|
||||
/** @var Token */
|
||||
public $openBrace;
|
||||
|
||||
/** @var Node[] */
|
||||
public $enumMemberDeclarations;
|
||||
|
||||
/** @var Token */
|
||||
public $closeBrace;
|
||||
|
||||
const CHILD_NAMES = [
|
||||
'openBrace',
|
||||
'enumMemberDeclarations',
|
||||
'closeBrace',
|
||||
];
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
namespace Microsoft\PhpParser\Node\Statement;
|
||||
|
||||
use Microsoft\PhpParser\ClassLike;
|
||||
use Microsoft\PhpParser\NamespacedNameInterface;
|
||||
use Microsoft\PhpParser\NamespacedNameTrait;
|
||||
use Microsoft\PhpParser\Node\AttributeGroup;
|
||||
use Microsoft\PhpParser\Node\StatementNode;
|
||||
use Microsoft\PhpParser\Node\EnumMembers;
|
||||
use Microsoft\PhpParser\Token;
|
||||
|
||||
class EnumDeclaration extends StatementNode implements NamespacedNameInterface, ClassLike {
|
||||
use NamespacedNameTrait;
|
||||
|
||||
/** @var AttributeGroup[]|null */
|
||||
public $attributes;
|
||||
|
||||
/** @var Token */
|
||||
public $enumKeyword;
|
||||
|
||||
/** @var Token */
|
||||
public $name;
|
||||
|
||||
/** @var Token|null */
|
||||
public $colonToken;
|
||||
|
||||
/** @var Token|null */
|
||||
public $enumType;
|
||||
|
||||
/** @var EnumMembers */
|
||||
public $enumMembers;
|
||||
|
||||
const CHILD_NAMES = [
|
||||
'attributes',
|
||||
'enumKeyword',
|
||||
'name',
|
||||
'colonToken',
|
||||
'enumType',
|
||||
'enumMembers',
|
||||
];
|
||||
|
||||
public function getNameParts() : array {
|
||||
return [$this->name];
|
||||
}
|
||||
}
|
|
@ -20,4 +20,5 @@ class ParseContext {
|
|||
const InterfaceMembers = 10;
|
||||
const TraitMembers = 11;
|
||||
const Count = 12;
|
||||
const EnumMembers = 13;
|
||||
}
|
||||
|
|
134
src/Parser.php
134
src/Parser.php
|
@ -16,6 +16,8 @@ use Microsoft\PhpParser\Node\ClassBaseClause;
|
|||
use Microsoft\PhpParser\Node\ClassInterfaceClause;
|
||||
use Microsoft\PhpParser\Node\ClassMembersNode;
|
||||
use Microsoft\PhpParser\Node\ConstElement;
|
||||
use Microsoft\PhpParser\Node\EnumCaseDeclaration;
|
||||
use Microsoft\PhpParser\Node\EnumMembers;
|
||||
use Microsoft\PhpParser\Node\Expression;
|
||||
use Microsoft\PhpParser\Node\Expression\{
|
||||
AnonymousFunctionCreationExpression,
|
||||
|
@ -88,6 +90,7 @@ use Microsoft\PhpParser\Node\Statement\{
|
|||
DeclareStatement,
|
||||
DoStatement,
|
||||
EmptyStatement,
|
||||
EnumDeclaration,
|
||||
ExpressionStatement,
|
||||
ForeachStatement,
|
||||
ForStatement,
|
||||
|
@ -292,6 +295,7 @@ class Parser {
|
|||
case ParseContext::ClassMembers:
|
||||
case ParseContext::BlockStatements:
|
||||
case ParseContext::TraitMembers:
|
||||
case ParseContext::EnumMembers:
|
||||
return $tokenKind === TokenKind::CloseBraceToken;
|
||||
case ParseContext::SwitchStatementElements:
|
||||
return $tokenKind === TokenKind::CloseBraceToken || $tokenKind === TokenKind::EndSwitchKeyword;
|
||||
|
@ -343,6 +347,9 @@ class Parser {
|
|||
case ParseContext::TraitMembers:
|
||||
return $this->isTraitMemberDeclarationStart($token);
|
||||
|
||||
case ParseContext::EnumMembers:
|
||||
return $this->isEnumMemberDeclarationStart($token);
|
||||
|
||||
case ParseContext::InterfaceMembers:
|
||||
return $this->isInterfaceMemberDeclarationStart($token);
|
||||
|
||||
|
@ -374,6 +381,9 @@ class Parser {
|
|||
case ParseContext::InterfaceMembers:
|
||||
return $this->parseInterfaceElementFn();
|
||||
|
||||
case ParseContext::EnumMembers:
|
||||
return $this->parseEnumElementFn();
|
||||
|
||||
case ParseContext::SwitchStatementElements:
|
||||
return $this->parseCaseOrDefaultStatement();
|
||||
default:
|
||||
|
@ -583,6 +593,9 @@ class Parser {
|
|||
case TokenKind::TraitKeyword:
|
||||
return $this->parseTraitDeclaration($parentNode);
|
||||
|
||||
case TokenKind::EnumKeyword:
|
||||
return $this->parseEnumDeclaration($parentNode);
|
||||
|
||||
// global-declaration
|
||||
case TokenKind::GlobalKeyword:
|
||||
return $this->parseGlobalDeclaration($parentNode);
|
||||
|
@ -715,12 +728,15 @@ class Parser {
|
|||
} elseif ($parentNode instanceof TraitMembers) {
|
||||
// Create a trait element or a MissingMemberDeclaration
|
||||
$statement = $this->parseTraitElementFn()($parentNode);
|
||||
} elseif ($parentNode instanceof EnumMembers) {
|
||||
// Create a enum element or a MissingMemberDeclaration
|
||||
$statement = $this->parseEnumElementFn()($parentNode);
|
||||
} elseif ($parentNode instanceof InterfaceMembers) {
|
||||
// Create an interface element or a MissingMemberDeclaration
|
||||
$statement = $this->parseInterfaceElementFn()($parentNode);
|
||||
} else {
|
||||
// Classlikes, anonymous functions, global functions, and arrow functions can have attributes. Global constants cannot.
|
||||
if (in_array($this->token->kind, [TokenKind::ClassKeyword, TokenKind::TraitKeyword, TokenKind::InterfaceKeyword, TokenKind::AbstractKeyword, TokenKind::FinalKeyword, TokenKind::FunctionKeyword, TokenKind::FnKeyword], true) ||
|
||||
if (in_array($this->token->kind, [TokenKind::ClassKeyword, TokenKind::TraitKeyword, TokenKind::InterfaceKeyword, TokenKind::AbstractKeyword, TokenKind::FinalKeyword, TokenKind::FunctionKeyword, TokenKind::FnKeyword, TokenKind::EnumKeyword], true) ||
|
||||
$this->token->kind === TokenKind::StaticKeyword && $this->lookahead([TokenKind::FunctionKeyword, TokenKind::FnKeyword])) {
|
||||
$statement = $this->parseStatement($parentNode);
|
||||
} else {
|
||||
|
@ -734,6 +750,8 @@ class Parser {
|
|||
if ($statement instanceof FunctionLike ||
|
||||
$statement instanceof ClassDeclaration ||
|
||||
$statement instanceof TraitDeclaration ||
|
||||
$statement instanceof EnumDeclaration ||
|
||||
$statement instanceof EnumCaseDeclaration ||
|
||||
$statement instanceof InterfaceDeclaration ||
|
||||
$statement instanceof ClassConstDeclaration ||
|
||||
$statement instanceof PropertyDeclaration ||
|
||||
|
@ -1012,6 +1030,9 @@ class Parser {
|
|||
// trait-declaration
|
||||
case TokenKind::TraitKeyword:
|
||||
|
||||
// enum-declaration
|
||||
case TokenKind::EnumKeyword:
|
||||
|
||||
// namespace-definition
|
||||
case TokenKind::NamespaceKeyword:
|
||||
|
||||
|
@ -3198,6 +3219,21 @@ class Parser {
|
|||
return $classConstDeclaration;
|
||||
}
|
||||
|
||||
private function parseEnumCaseDeclaration($parentNode) {
|
||||
$classConstDeclaration = new EnumCaseDeclaration();
|
||||
$classConstDeclaration->parent = $parentNode;
|
||||
$classConstDeclaration->caseKeyword = $this->eat1(TokenKind::CaseKeyword);
|
||||
$classConstDeclaration->name = $this->eat($this->nameOrKeywordOrReservedWordTokens);
|
||||
$classConstDeclaration->equalsToken = $this->eatOptional1(TokenKind::EqualsToken);
|
||||
if ($classConstDeclaration->equalsToken !== null) {
|
||||
// TODO add post-parse rule that checks for invalid assignments
|
||||
$classConstDeclaration->assignment = $this->parseExpression($classConstDeclaration);
|
||||
}
|
||||
$classConstDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken);
|
||||
|
||||
return $classConstDeclaration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $parentNode
|
||||
* @param Token[] $modifiers
|
||||
|
@ -3520,6 +3556,102 @@ class Parser {
|
|||
};
|
||||
}
|
||||
|
||||
private function parseEnumDeclaration($parentNode) {
|
||||
$enumDeclaration = new EnumDeclaration();
|
||||
$enumDeclaration->parent = $parentNode;
|
||||
|
||||
$enumDeclaration->enumKeyword = $this->eat1(TokenKind::EnumKeyword);
|
||||
$enumDeclaration->name = $this->eat1(TokenKind::Name);
|
||||
$enumDeclaration->colonToken = $this->eatOptional1(TokenKind::ColonToken);
|
||||
if ($enumDeclaration->colonToken !== null) {
|
||||
$enumDeclaration->enumType = $this->tryParseParameterTypeDeclaration($enumDeclaration)
|
||||
?: new MissingToken(TokenKind::EnumType, $this->token->fullStart);
|
||||
}
|
||||
|
||||
$enumDeclaration->enumMembers = $this->parseEnumMembers($enumDeclaration);
|
||||
|
||||
return $enumDeclaration;
|
||||
}
|
||||
|
||||
private function parseEnumMembers($parentNode) {
|
||||
$enumMembers = new EnumMembers();
|
||||
$enumMembers->parent = $parentNode;
|
||||
|
||||
$enumMembers->openBrace = $this->eat1(TokenKind::OpenBraceToken);
|
||||
|
||||
$enumMembers->enumMemberDeclarations = $this->parseList($enumMembers, ParseContext::EnumMembers);
|
||||
|
||||
$enumMembers->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
|
||||
|
||||
return $enumMembers;
|
||||
}
|
||||
|
||||
private function isEnumMemberDeclarationStart($token) {
|
||||
switch ($token->kind) {
|
||||
// modifiers
|
||||
case TokenKind::PublicKeyword:
|
||||
case TokenKind::ProtectedKeyword:
|
||||
case TokenKind::PrivateKeyword:
|
||||
// case TokenKind::VarKeyword:
|
||||
case TokenKind::StaticKeyword:
|
||||
case TokenKind::AbstractKeyword:
|
||||
case TokenKind::FinalKeyword:
|
||||
|
||||
// method-declaration
|
||||
case TokenKind::FunctionKeyword:
|
||||
|
||||
// trait-use-clauses (enums can use traits)
|
||||
case TokenKind::UseKeyword:
|
||||
|
||||
// cases and constants
|
||||
case TokenKind::CaseKeyword:
|
||||
case TokenKind::ConstKeyword:
|
||||
|
||||
// attributes
|
||||
case TokenKind::AttributeToken:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function parseEnumElementFn() {
|
||||
return function ($parentNode) {
|
||||
$modifiers = $this->parseModifiers();
|
||||
|
||||
$token = $this->getCurrentToken();
|
||||
switch ($token->kind) {
|
||||
// TODO: CaseKeyword
|
||||
case TokenKind::CaseKeyword:
|
||||
return $this->parseEnumCaseDeclaration($parentNode);
|
||||
|
||||
case TokenKind::ConstKeyword:
|
||||
return $this->parseClassConstDeclaration($parentNode, $modifiers);
|
||||
|
||||
case TokenKind::FunctionKeyword:
|
||||
return $this->parseMethodDeclaration($parentNode, $modifiers);
|
||||
|
||||
case TokenKind::QuestionToken:
|
||||
return $this->parseRemainingPropertyDeclarationOrMissingMemberDeclaration(
|
||||
$parentNode,
|
||||
$modifiers,
|
||||
$this->eat1(TokenKind::QuestionToken)
|
||||
);
|
||||
case TokenKind::VariableName:
|
||||
return $this->parsePropertyDeclaration($parentNode, $modifiers);
|
||||
|
||||
case TokenKind::UseKeyword:
|
||||
return $this->parseTraitUseClause($parentNode);
|
||||
|
||||
case TokenKind::AttributeToken:
|
||||
return $this->parseAttributeStatement($parentNode);
|
||||
|
||||
default:
|
||||
return $this->parseRemainingPropertyDeclarationOrMissingMemberDeclaration($parentNode, $modifiers);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Node $parentNode
|
||||
* @param Token[] $modifiers
|
||||
|
|
|
@ -12,8 +12,10 @@ define(__NAMESPACE__ . '\T_COALESCE_EQUAL', defined('T_COALESCE_EQUAL') ? consta
|
|||
define(__NAMESPACE__ . '\T_FN', defined('T_FN') ? constant('T_FN') : 'T_FN');
|
||||
// If this predates PHP 8.0, T_MATCH is unavailable. The replacement value is arbitrary - it just has to be different from other values of token constants.
|
||||
define(__NAMESPACE__ . '\T_MATCH', defined('T_MATCH') ? constant('T_MATCH') : 'T_MATCH');
|
||||
define(__NAMESPACE__ . '\T_NULLSAFE_OBJECT_OPERATOR', defined('T_NULLSAFE_OBJECT_OPERATOR') ? constant('T_NULLSAFE_OBJECT_OPERATOR') : 'T_MATCH');
|
||||
define(__NAMESPACE__ . '\T_NULLSAFE_OBJECT_OPERATOR', defined('T_NULLSAFE_OBJECT_OPERATOR') ? constant('T_NULLSAFE_OBJECT_OPERATOR') : 'T_NULLSAFE_OBJECT_OPERATOR');
|
||||
define(__NAMESPACE__ . '\T_ATTRIBUTE', defined('T_ATTRIBUTE') ? constant('T_ATTRIBUTE') : 'T_ATTRIBUTE');
|
||||
// If this predates PHP 8.1, T_ENUM is unavailable. The replacement value is arbitrary - it just has to be different from other values of token constants.
|
||||
define(__NAMESPACE__ . '\T_ENUM', defined('T_ENUM') ? constant('T_ENUM') : 'T_ENUM');
|
||||
|
||||
/**
|
||||
* Tokenizes content using PHP's built-in `token_get_all`, and converts to "lightweight" Token representation.
|
||||
|
@ -257,6 +259,7 @@ class PhpTokenizer implements TokenStreamProviderInterface {
|
|||
T_ENDIF => TokenKind::EndIfKeyword,
|
||||
T_ENDSWITCH => TokenKind::EndSwitchKeyword,
|
||||
T_ENDWHILE => TokenKind::EndWhileKeyword,
|
||||
T_ENUM => TokenKind::EnumKeyword,
|
||||
T_EVAL => TokenKind::EvalKeyword,
|
||||
T_EXIT => TokenKind::ExitKeyword,
|
||||
T_EXTENDS => TokenKind::ExtendsKeyword,
|
||||
|
|
|
@ -89,6 +89,7 @@ class TokenKind {
|
|||
const MatchKeyword = 169;
|
||||
/** @deprecated use IterableReservedWord */
|
||||
const IterableKeyword = self::IterableReservedWord;
|
||||
const EnumKeyword = 171;
|
||||
|
||||
const OpenBracketToken = 201;
|
||||
const CloseBracketToken = 202;
|
||||
|
@ -195,6 +196,7 @@ class TokenKind {
|
|||
const ReturnType = 336;
|
||||
const InlineHtml = 337;
|
||||
const PropertyType = 338;
|
||||
const EnumType = 339;
|
||||
|
||||
// const DollarOpenCurly = 339;
|
||||
const EncapsedAndWhitespace = 400;
|
||||
|
|
|
@ -36,6 +36,7 @@ class TokenStringMaps {
|
|||
"endif" => TokenKind::EndIfKeyword,
|
||||
"endswitch" => TokenKind::EndSwitchKeyword,
|
||||
"endwhile" => TokenKind::EndWhileKeyword,
|
||||
"enum" => TokenKind::EnumKeyword,
|
||||
"eval" => TokenKind::EvalKeyword,
|
||||
"exit" => TokenKind::ExitKeyword,
|
||||
"extends" => TokenKind::ExtendsKeyword,
|
||||
|
|
|
@ -76,6 +76,13 @@ class ParserGrammarTest extends TestCase {
|
|||
const FILE_PATTERN = __DIR__ . "/cases/parser/*";
|
||||
const PHP74_FILE_PATTERN = __DIR__ . "/cases/parser74/*";
|
||||
const PHP80_FILE_PATTERN = __DIR__ . "/cases/parser80/*";
|
||||
const PHP81_FILE_PATTERN = __DIR__ . "/cases/parser81/*";
|
||||
|
||||
const PATTERNS_FOR_MINIMUM_PHP_VERSION = [
|
||||
[70400, self::PHP74_FILE_PATTERN],
|
||||
[80000, self::PHP80_FILE_PATTERN],
|
||||
[80100, self::PHP81_FILE_PATTERN],
|
||||
];
|
||||
|
||||
public function treeProvider() {
|
||||
$testCases = glob(self::FILE_PATTERN . ".php");
|
||||
|
@ -89,22 +96,14 @@ class ParserGrammarTest extends TestCase {
|
|||
$testProviderArray[basename($testCase)] = [$testCase, $testCase . ".tree", $testCase . ".diag"];
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 70400) {
|
||||
// There are some test cases that depend on the php 7.3/php 7.4 lexer (e.g. the `??=` token).
|
||||
// If this project goes that route, these could be moved in the regular parser/ directory.
|
||||
// - It might be possible to emulate being able to parse this token instead (e.g. merge tokens if strpos($contents, `??=`) is not false.
|
||||
$testCases = glob(self::PHP74_FILE_PATTERN . ".php");
|
||||
foreach ($testCases as $testCase) {
|
||||
$testProviderArray[basename($testCase)] = [$testCase, $testCase . ".tree", $testCase . ".diag"];
|
||||
foreach (self::PATTERNS_FOR_MINIMUM_PHP_VERSION as list($minVersionId, $filePattern)) {
|
||||
if (PHP_VERSION_ID >= $minVersionId) {
|
||||
$testCases = glob($filePattern . ".php");
|
||||
foreach ($testCases as $testCase) {
|
||||
$testProviderArray[basename($testCase)] = [$testCase, $testCase . ".tree", $testCase . ".diag"];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (PHP_VERSION_ID >= 80000) {
|
||||
$testCases = glob(self::PHP80_FILE_PATTERN . ".php");
|
||||
foreach ($testCases as $testCase) {
|
||||
$testProviderArray[basename($testCase)] = [$testCase, $testCase . ".tree", $testCase . ".diag"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $testProviderArray;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<?php
|
||||
enum X {}
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"textLength": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"EnumDeclaration": {
|
||||
"attributes": null,
|
||||
"enumKeyword": {
|
||||
"kind": "EnumKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 1
|
||||
},
|
||||
"colonToken": null,
|
||||
"enumType": null,
|
||||
"enumMembers": {
|
||||
"EnumMembers": {
|
||||
"openBrace": {
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"enumMemberDeclarations": [],
|
||||
"closeBrace": {
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
enum X: int {
|
||||
const X = 123;
|
||||
public function test() {};
|
||||
function other();
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "'}' expected.",
|
||||
"start": 68,
|
||||
"length": 0
|
||||
},
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "'{' expected.",
|
||||
"start": 90,
|
||||
"length": 0
|
||||
}
|
||||
]
|
|
@ -0,0 +1,200 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"textLength": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"EnumDeclaration": {
|
||||
"attributes": null,
|
||||
"enumKeyword": {
|
||||
"kind": "EnumKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 1
|
||||
},
|
||||
"colonToken": {
|
||||
"kind": "ColonToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"enumType": {
|
||||
"kind": "IntReservedWord",
|
||||
"textLength": 3
|
||||
},
|
||||
"enumMembers": {
|
||||
"EnumMembers": {
|
||||
"openBrace": {
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"enumMemberDeclarations": [
|
||||
{
|
||||
"ClassConstDeclaration": {
|
||||
"attributes": null,
|
||||
"modifiers": [],
|
||||
"constKeyword": {
|
||||
"kind": "ConstKeyword",
|
||||
"textLength": 5
|
||||
},
|
||||
"constElements": {
|
||||
"ConstElementList": {
|
||||
"children": [
|
||||
{
|
||||
"ConstElement": {
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 1
|
||||
},
|
||||
"equalsToken": {
|
||||
"kind": "EqualsToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"assignment": {
|
||||
"NumericLiteral": {
|
||||
"children": {
|
||||
"kind": "IntegerLiteralToken",
|
||||
"textLength": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"semicolon": {
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"MethodDeclaration": {
|
||||
"attributes": null,
|
||||
"modifiers": [
|
||||
{
|
||||
"kind": "PublicKeyword",
|
||||
"textLength": 6
|
||||
}
|
||||
],
|
||||
"functionKeyword": {
|
||||
"kind": "FunctionKeyword",
|
||||
"textLength": 8
|
||||
},
|
||||
"byRefToken": null,
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 4
|
||||
},
|
||||
"openParen": {
|
||||
"kind": "OpenParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"parameters": null,
|
||||
"closeParen": {
|
||||
"kind": "CloseParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"colonToken": null,
|
||||
"questionToken": null,
|
||||
"returnType": null,
|
||||
"otherReturnTypes": null,
|
||||
"compoundStatementOrSemicolon": {
|
||||
"CompoundStatementNode": {
|
||||
"openBrace": {
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"statements": [],
|
||||
"closeBrace": {
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"closeBrace": {
|
||||
"error": "MissingToken",
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"EmptyStatement": {
|
||||
"semicolon": {
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"FunctionDeclaration": {
|
||||
"attributes": null,
|
||||
"functionKeyword": {
|
||||
"kind": "FunctionKeyword",
|
||||
"textLength": 8
|
||||
},
|
||||
"byRefToken": null,
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 5
|
||||
},
|
||||
"openParen": {
|
||||
"kind": "OpenParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"parameters": null,
|
||||
"closeParen": {
|
||||
"kind": "CloseParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"colonToken": null,
|
||||
"questionToken": null,
|
||||
"returnType": null,
|
||||
"otherReturnTypes": null,
|
||||
"compoundStatementOrSemicolon": {
|
||||
"CompoundStatementNode": {
|
||||
"openBrace": {
|
||||
"error": "MissingToken",
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 0
|
||||
},
|
||||
"statements": [
|
||||
{
|
||||
"EmptyStatement": {
|
||||
"semicolon": {
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"closeBrace": {
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
#[MyAttr]
|
||||
enum X {
|
||||
case A;
|
||||
#[MyAttribute]
|
||||
case B;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,163 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"textLength": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"EnumDeclaration": {
|
||||
"attributes": [
|
||||
{
|
||||
"AttributeGroup": {
|
||||
"startToken": {
|
||||
"kind": "AttributeToken",
|
||||
"textLength": 2
|
||||
},
|
||||
"attributes": {
|
||||
"AttributeElementList": {
|
||||
"children": [
|
||||
{
|
||||
"Attribute": {
|
||||
"name": {
|
||||
"QualifiedName": {
|
||||
"globalSpecifier": null,
|
||||
"relativeSpecifier": null,
|
||||
"nameParts": [
|
||||
{
|
||||
"kind": "Name",
|
||||
"textLength": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"openParen": null,
|
||||
"argumentExpressionList": null,
|
||||
"closeParen": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"endToken": {
|
||||
"kind": "CloseBracketToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"enumKeyword": {
|
||||
"kind": "EnumKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 1
|
||||
},
|
||||
"colonToken": null,
|
||||
"enumType": null,
|
||||
"enumMembers": {
|
||||
"EnumMembers": {
|
||||
"openBrace": {
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"enumMemberDeclarations": [
|
||||
{
|
||||
"EnumCaseDeclaration": {
|
||||
"attributes": null,
|
||||
"caseKeyword": {
|
||||
"kind": "CaseKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 1
|
||||
},
|
||||
"equalsToken": null,
|
||||
"assignment": null,
|
||||
"semicolon": {
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"EnumCaseDeclaration": {
|
||||
"attributes": [
|
||||
{
|
||||
"AttributeGroup": {
|
||||
"startToken": {
|
||||
"kind": "AttributeToken",
|
||||
"textLength": 2
|
||||
},
|
||||
"attributes": {
|
||||
"AttributeElementList": {
|
||||
"children": [
|
||||
{
|
||||
"Attribute": {
|
||||
"name": {
|
||||
"QualifiedName": {
|
||||
"globalSpecifier": null,
|
||||
"relativeSpecifier": null,
|
||||
"nameParts": [
|
||||
{
|
||||
"kind": "Name",
|
||||
"textLength": 11
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"openParen": null,
|
||||
"argumentExpressionList": null,
|
||||
"closeParen": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"endToken": {
|
||||
"kind": "CloseBracketToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"caseKeyword": {
|
||||
"kind": "CaseKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 1
|
||||
},
|
||||
"equalsToken": null,
|
||||
"assignment": null,
|
||||
"semicolon": {
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"closeBrace": {
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
enum X: string {
|
||||
case Hearts = 'H';
|
||||
static function example() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"textLength": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"EnumDeclaration": {
|
||||
"attributes": null,
|
||||
"enumKeyword": {
|
||||
"kind": "EnumKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 1
|
||||
},
|
||||
"colonToken": {
|
||||
"kind": "ColonToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"enumType": {
|
||||
"kind": "StringReservedWord",
|
||||
"textLength": 6
|
||||
},
|
||||
"enumMembers": {
|
||||
"EnumMembers": {
|
||||
"openBrace": {
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"enumMemberDeclarations": [
|
||||
{
|
||||
"EnumCaseDeclaration": {
|
||||
"attributes": null,
|
||||
"caseKeyword": {
|
||||
"kind": "CaseKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 6
|
||||
},
|
||||
"equalsToken": {
|
||||
"kind": "EqualsToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"assignment": {
|
||||
"StringLiteral": {
|
||||
"startQuote": null,
|
||||
"children": {
|
||||
"kind": "StringLiteralToken",
|
||||
"textLength": 3
|
||||
},
|
||||
"endQuote": null
|
||||
}
|
||||
},
|
||||
"semicolon": {
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"MethodDeclaration": {
|
||||
"attributes": null,
|
||||
"modifiers": [
|
||||
{
|
||||
"kind": "StaticKeyword",
|
||||
"textLength": 6
|
||||
}
|
||||
],
|
||||
"functionKeyword": {
|
||||
"kind": "FunctionKeyword",
|
||||
"textLength": 8
|
||||
},
|
||||
"byRefToken": null,
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 7
|
||||
},
|
||||
"openParen": {
|
||||
"kind": "OpenParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"parameters": null,
|
||||
"closeParen": {
|
||||
"kind": "CloseParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"colonToken": null,
|
||||
"questionToken": null,
|
||||
"returnType": null,
|
||||
"otherReturnTypes": null,
|
||||
"compoundStatementOrSemicolon": {
|
||||
"CompoundStatementNode": {
|
||||
"openBrace": {
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"statements": [],
|
||||
"closeBrace": {
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"closeBrace": {
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
// missing type
|
||||
enum Foo : {
|
||||
// missing expression
|
||||
case X = ;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "'EnumType' expected.",
|
||||
"start": 32,
|
||||
"length": 0
|
||||
},
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "'Expression' expected.",
|
||||
"start": 73,
|
||||
"length": 0
|
||||
}
|
||||
]
|
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"textLength": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"EnumDeclaration": {
|
||||
"attributes": null,
|
||||
"enumKeyword": {
|
||||
"kind": "EnumKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 3
|
||||
},
|
||||
"colonToken": {
|
||||
"kind": "ColonToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"enumType": {
|
||||
"error": "MissingToken",
|
||||
"kind": "EnumType",
|
||||
"textLength": 0
|
||||
},
|
||||
"enumMembers": {
|
||||
"EnumMembers": {
|
||||
"openBrace": {
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"enumMemberDeclarations": [
|
||||
{
|
||||
"EnumCaseDeclaration": {
|
||||
"attributes": null,
|
||||
"caseKeyword": {
|
||||
"kind": "CaseKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 1
|
||||
},
|
||||
"equalsToken": {
|
||||
"kind": "EqualsToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"assignment": {
|
||||
"error": "MissingToken",
|
||||
"kind": "Expression",
|
||||
"textLength": 0
|
||||
},
|
||||
"semicolon": {
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"closeBrace": {
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
// union type syntax is not allowed by php when parsing enum type.
|
||||
enum Foo: int|string {
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
[
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "'{' expected.",
|
||||
"start": 86,
|
||||
"length": 0
|
||||
},
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "Unexpected '|'",
|
||||
"start": 86,
|
||||
"length": 1
|
||||
},
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "'}' expected.",
|
||||
"start": 87,
|
||||
"length": 0
|
||||
},
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "';' expected.",
|
||||
"start": 97,
|
||||
"length": 0
|
||||
}
|
||||
]
|
|
@ -0,0 +1,96 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"textLength": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"EnumDeclaration": {
|
||||
"attributes": null,
|
||||
"enumKeyword": {
|
||||
"kind": "EnumKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"name": {
|
||||
"kind": "Name",
|
||||
"textLength": 3
|
||||
},
|
||||
"colonToken": {
|
||||
"kind": "ColonToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"enumType": {
|
||||
"kind": "IntReservedWord",
|
||||
"textLength": 3
|
||||
},
|
||||
"enumMembers": {
|
||||
"EnumMembers": {
|
||||
"openBrace": {
|
||||
"error": "MissingToken",
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 0
|
||||
},
|
||||
"enumMemberDeclarations": [
|
||||
{
|
||||
"error": "SkippedToken",
|
||||
"kind": "BarToken",
|
||||
"textLength": 1
|
||||
}
|
||||
],
|
||||
"closeBrace": {
|
||||
"error": "MissingToken",
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ExpressionStatement": {
|
||||
"expression": {
|
||||
"SubscriptExpression": {
|
||||
"postfixExpression": {
|
||||
"QualifiedName": {
|
||||
"globalSpecifier": null,
|
||||
"relativeSpecifier": null,
|
||||
"nameParts": [
|
||||
{
|
||||
"kind": "Name",
|
||||
"textLength": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"openBracketOrBrace": {
|
||||
"kind": "OpenBraceToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"accessExpression": null,
|
||||
"closeBracketOrBrace": {
|
||||
"kind": "CloseBraceToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"semicolon": {
|
||||
"error": "MissingToken",
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче