Code Clean up, more testing
Remove unused code and classes. Clean up code to remove all named constant from them and throw deprecation alerts if used. Add basic psalm setup in root folder and remove from www folder
This commit is contained in:
276
vendor/microsoft/tolerant-php-parser/src/Parser.php
vendored
276
vendor/microsoft/tolerant-php-parser/src/Parser.php
vendored
@@ -6,6 +6,7 @@
|
||||
|
||||
namespace Microsoft\PhpParser;
|
||||
|
||||
use Closure;
|
||||
use Microsoft\PhpParser\Node\AnonymousFunctionUseClause;
|
||||
use Microsoft\PhpParser\Node\ArrayElement;
|
||||
use Microsoft\PhpParser\Node\Attribute;
|
||||
@@ -70,6 +71,7 @@ use Microsoft\PhpParser\Node\MissingMemberDeclaration;
|
||||
use Microsoft\PhpParser\Node\NamespaceAliasingClause;
|
||||
use Microsoft\PhpParser\Node\NamespaceUseGroupClause;
|
||||
use Microsoft\PhpParser\Node\NumericLiteral;
|
||||
use Microsoft\PhpParser\Node\ParenthesizedIntersectionType;
|
||||
use Microsoft\PhpParser\Node\PropertyDeclaration;
|
||||
use Microsoft\PhpParser\Node\ReservedWord;
|
||||
use Microsoft\PhpParser\Node\StringLiteral;
|
||||
@@ -95,6 +97,7 @@ use Microsoft\PhpParser\Node\Statement\{
|
||||
ForStatement,
|
||||
FunctionDeclaration,
|
||||
GotoStatement,
|
||||
HaltCompilerStatement,
|
||||
IfStatementNode,
|
||||
InlineHtml,
|
||||
InterfaceDeclaration,
|
||||
@@ -144,8 +147,9 @@ class Parser {
|
||||
[TokenKind::ArrayKeyword, TokenKind::CallableKeyword, TokenKind::BoolReservedWord,
|
||||
TokenKind::FloatReservedWord, TokenKind::IntReservedWord, TokenKind::StringReservedWord,
|
||||
TokenKind::ObjectReservedWord, TokenKind::NullReservedWord, TokenKind::FalseReservedWord,
|
||||
TokenKind::IterableReservedWord, TokenKind::MixedReservedWord]; // TODO update spec
|
||||
$this->returnTypeDeclarationTokens = \array_merge([TokenKind::VoidReservedWord, TokenKind::NullReservedWord, TokenKind::FalseReservedWord, TokenKind::StaticKeyword], $this->parameterTypeDeclarationTokens);
|
||||
TokenKind::TrueReservedWord, TokenKind::IterableReservedWord, TokenKind::MixedReservedWord,
|
||||
TokenKind::VoidReservedWord, TokenKind::NeverReservedWord]; // TODO update spec
|
||||
$this->returnTypeDeclarationTokens = \array_merge([TokenKind::StaticKeyword], $this->parameterTypeDeclarationTokens);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -557,10 +561,8 @@ class Parser {
|
||||
// class-declaration
|
||||
case TokenKind::FinalKeyword:
|
||||
case TokenKind::AbstractKeyword:
|
||||
if (!$this->lookahead(TokenKind::ClassKeyword)) {
|
||||
$this->advanceToken();
|
||||
return new SkippedToken($token);
|
||||
}
|
||||
case TokenKind::ReadonlyKeyword:
|
||||
// fallthrough
|
||||
case TokenKind::ClassKeyword:
|
||||
return $this->parseClassDeclaration($parentNode);
|
||||
|
||||
@@ -614,6 +616,15 @@ class Parser {
|
||||
|
||||
case TokenKind::UnsetKeyword:
|
||||
return $this->parseUnsetStatement($parentNode);
|
||||
|
||||
case TokenKind::HaltCompilerKeyword:
|
||||
if ($parentNode instanceof SourceFileNode) {
|
||||
return $this->parseHaltCompilerStatement($parentNode);
|
||||
}
|
||||
// __halt_compiler is a fatal compile error anywhere other than the top level.
|
||||
// It won't be seen elsewhere in other programs - warn about the token being unexpected.
|
||||
$this->advanceToken();
|
||||
return new SkippedToken($token);
|
||||
}
|
||||
|
||||
$expressionStatement = new ExpressionStatement();
|
||||
@@ -657,10 +668,20 @@ class Parser {
|
||||
};
|
||||
}
|
||||
|
||||
/** @return Token[] */
|
||||
private function parseClassModifiers(): array {
|
||||
$modifiers = [];
|
||||
while ($token = $this->eatOptional(TokenKind::AbstractKeyword, TokenKind::FinalKeyword, TokenKind::ReadonlyKeyword)) {
|
||||
$modifiers[] = $token;
|
||||
}
|
||||
return $modifiers;
|
||||
}
|
||||
|
||||
private function parseClassDeclaration($parentNode) : Node {
|
||||
$classNode = new ClassDeclaration(); // TODO verify not nested
|
||||
$classNode->parent = $parentNode;
|
||||
$classNode->abstractOrFinalModifier = $this->eatOptional(TokenKind::AbstractKeyword, TokenKind::FinalKeyword);
|
||||
$classNode->abstractOrFinalModifier = $this->eatOptional(TokenKind::AbstractKeyword, TokenKind::FinalKeyword, TokenKind::ReadonlyKeyword);
|
||||
$classNode->modifiers = $this->parseClassModifiers();
|
||||
$classNode->classKeyword = $this->eat1(TokenKind::ClassKeyword);
|
||||
$classNode->name = $this->eat($this->nameOrReservedWordTokens); // TODO should be any
|
||||
$classNode->name->kind = TokenKind::Name;
|
||||
@@ -838,9 +859,6 @@ class Parser {
|
||||
if (end($children) instanceof MissingToken && ($children[\count($children) - 2]->kind ?? null) === TokenKind::AmpersandToken) {
|
||||
array_pop($parameter->typeDeclarationList->children);
|
||||
$parameter->byRefToken = array_pop($parameter->typeDeclarationList->children);
|
||||
if (!$parameter->typeDeclarationList->children) {
|
||||
unset($parameter->typeDeclarationList);
|
||||
}
|
||||
}
|
||||
} elseif ($parameter->questionToken) {
|
||||
// TODO ParameterType?
|
||||
@@ -882,27 +900,22 @@ class Parser {
|
||||
/**
|
||||
* Attempt to parse the return type after the `:` and optional `?` token.
|
||||
*
|
||||
* TODO: Consider changing the return type to a new class TypeList in a future major release?
|
||||
* ParenthesizedIntersectionType is not a qualified name.
|
||||
* @return DelimitedList\QualifiedNameList|null
|
||||
*/
|
||||
private function parseReturnTypeDeclarationList($parentNode) {
|
||||
$result = $this->parseDelimitedList(
|
||||
DelimitedList\QualifiedNameList::class,
|
||||
self::TYPE_DELIMITER_TOKENS,
|
||||
function ($token) {
|
||||
return \in_array($token->kind, $this->returnTypeDeclarationTokens, true) || $this->isQualifiedNameStart($token);
|
||||
return $this->parseUnionTypeDeclarationList(
|
||||
$parentNode,
|
||||
function ($token): bool {
|
||||
return \in_array($token->kind, $this->returnTypeDeclarationTokens, true) ||
|
||||
$this->isQualifiedNameStart($token);
|
||||
},
|
||||
function ($parentNode) {
|
||||
return $this->parseReturnTypeDeclaration($parentNode);
|
||||
},
|
||||
$parentNode,
|
||||
false);
|
||||
|
||||
// Add a MissingToken so that this will warn about `function () : T| {}`
|
||||
// TODO: Make this a reusable abstraction?
|
||||
if ($result && in_array(end($result->children)->kind ?? null, self::TYPE_DELIMITER_TOKENS)) {
|
||||
$result->children[] = new MissingToken(TokenKind::ReturnType, $this->token->fullStart);
|
||||
}
|
||||
return $result;
|
||||
TokenKind::ReturnType
|
||||
);
|
||||
}
|
||||
|
||||
private function parseReturnTypeDeclaration($parentNode) {
|
||||
@@ -917,28 +930,109 @@ class Parser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a union type such as A, A|B, A&B, A|(B&C), rejecting invalid syntax combinations.
|
||||
*
|
||||
* @param Node $parentNode
|
||||
* @param Closure(Token):bool $isTypeStart
|
||||
* @param Closure(Node):(Node|Token|null) $parseType
|
||||
* @param int $expectedTypeKind expected kind for token type
|
||||
* @return DelimitedList\QualifiedNameList|null
|
||||
*/
|
||||
private function parseUnionTypeDeclarationList($parentNode, Closure $isTypeStart, Closure $parseType, int $expectedTypeKind) {
|
||||
$result = new DelimitedList\QualifiedNameList();
|
||||
$token = $this->getCurrentToken();
|
||||
$delimiter = self::TYPE_DELIMITER_TOKENS;
|
||||
do {
|
||||
if ($token->kind === TokenKind::OpenParenToken || $isTypeStart($token)) {
|
||||
// Forbid mixing A&(B&C) if '&' was already seen
|
||||
$openParen = in_array(TokenKind::BarToken, $delimiter, true)
|
||||
? $this->eatOptional(TokenKind::OpenParenToken)
|
||||
: null;
|
||||
if ($openParen) {
|
||||
$element = $this->parseParenthesizedIntersectionType($result, $openParen, $isTypeStart, $parseType);
|
||||
// Forbid mixing (A&B)&C by forbidding `&` separator after a parenthesized intersection type.
|
||||
$delimiter = [TokenKind::BarToken];
|
||||
} else {
|
||||
$element = $parseType($result);
|
||||
}
|
||||
$result->addElement($element);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
$delimiterToken = $this->eatOptional($delimiter);
|
||||
if ($delimiterToken !== null) {
|
||||
$result->addElement($delimiterToken);
|
||||
$delimiter = [$delimiterToken->kind];
|
||||
}
|
||||
$token = $this->getCurrentToken();
|
||||
} while ($delimiterToken !== null);
|
||||
|
||||
$result->parent = $parentNode;
|
||||
if ($result->children === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (in_array(end($result->children)->kind ?? null, $delimiter, true)) {
|
||||
// Add a MissingToken so that this will warn about `function () : T| {}`
|
||||
$result->children[] = new MissingToken($expectedTypeKind, $this->token->fullStart);
|
||||
} elseif (count($result->children) === 1 && $result->children[0] instanceof ParenthesizedIntersectionType) {
|
||||
// dnf types with parenthesized intersection types are a union type of at least 2 types.
|
||||
$result->children[] = new MissingToken(TokenKind::BarToken, $this->token->fullStart);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $parentNode
|
||||
* @param Token $openParen
|
||||
* @param Closure(Token):bool $isTypeStart
|
||||
* @param Closure(Node):(Node|Token|null) $parseType
|
||||
*/
|
||||
private function parseParenthesizedIntersectionType($parentNode, Token $openParen, Closure $isTypeStart, Closure $parseType): ParenthesizedIntersectionType {
|
||||
$node = new ParenthesizedIntersectionType();
|
||||
$node->parent = $parentNode;
|
||||
$node->openParen = $openParen;
|
||||
$node->children = $this->parseDelimitedList(
|
||||
DelimitedList\QualifiedNameList::class,
|
||||
TokenKind::AmpersandToken,
|
||||
$isTypeStart,
|
||||
$parseType,
|
||||
$node,
|
||||
true);
|
||||
if ($node->children) {
|
||||
// https://wiki.php.net/rfc/dnf_types
|
||||
if ((end($node->children->children)->kind ?? null) === TokenKind::OpenParenToken) {
|
||||
// Add a MissingToken so that this will Warn about `function (A|(B&) $x) {}`
|
||||
$node->children->children[] = new MissingToken(TokenKind::Name, $this->token->fullStart);
|
||||
} elseif (count($node->children->children) === 1) {
|
||||
// Must have at least 2 parts for A|(B&C)
|
||||
$node->children->children[] = new MissingToken(TokenKind::AmpersandToken, $this->token->fullStart);
|
||||
}
|
||||
} else {
|
||||
// Having less than 2 types (no types) in A|() is a parse error
|
||||
$node->children = new MissingToken(TokenKind::Name, $this->token->fullStart);
|
||||
}
|
||||
$node->closeParen = $this->eat(TokenKind::CloseParenToken);
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node|null $parentNode
|
||||
* @return DelimitedList\QualifiedNameList|null
|
||||
*/
|
||||
private function tryParseParameterTypeDeclarationList($parentNode) {
|
||||
$result = $this->parseDelimitedList(
|
||||
DelimitedList\QualifiedNameList::class,
|
||||
self::TYPE_DELIMITER_TOKENS,
|
||||
return $this->parseUnionTypeDeclarationList(
|
||||
$parentNode,
|
||||
function ($token) {
|
||||
return \in_array($token->kind, $this->parameterTypeDeclarationTokens, true) || $this->isQualifiedNameStart($token);
|
||||
return \in_array($token->kind, $this->parameterTypeDeclarationTokens, true) ||
|
||||
$this->isQualifiedNameStart($token);
|
||||
},
|
||||
function ($parentNode) {
|
||||
return $this->tryParseParameterTypeDeclaration($parentNode);
|
||||
},
|
||||
$parentNode,
|
||||
true);
|
||||
|
||||
// Add a MissingToken so that this will Warn about `function (T| $x) {}`
|
||||
// TODO: Make this a reusable abstraction?
|
||||
if ($result && in_array(end($result->children)->kind ?? null, self::TYPE_DELIMITER_TOKENS)) {
|
||||
$result->children[] = new MissingToken(TokenKind::Name, $this->token->fullStart);
|
||||
}
|
||||
return $result;
|
||||
TokenKind::Name
|
||||
);
|
||||
}
|
||||
|
||||
private function parseCompoundStatement($parentNode) {
|
||||
@@ -950,12 +1044,6 @@ class Parser {
|
||||
return $compoundStatement;
|
||||
}
|
||||
|
||||
private function array_push_list(& $array, $list) {
|
||||
foreach ($list as $item) {
|
||||
$array[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
private function isClassMemberDeclarationStart(Token $token) {
|
||||
switch ($token->kind) {
|
||||
// const-modifier
|
||||
@@ -1036,6 +1124,7 @@ class Parser {
|
||||
case TokenKind::ClassKeyword:
|
||||
case TokenKind::AbstractKeyword:
|
||||
case TokenKind::FinalKeyword:
|
||||
case TokenKind::ReadonlyKeyword:
|
||||
|
||||
// interface-declaration
|
||||
case TokenKind::InterfaceKeyword:
|
||||
@@ -1062,6 +1151,9 @@ class Parser {
|
||||
|
||||
// attributes
|
||||
case TokenKind::AttributeToken:
|
||||
|
||||
// __halt_compiler
|
||||
case TokenKind::HaltCompilerKeyword:
|
||||
return true;
|
||||
|
||||
default:
|
||||
@@ -1324,8 +1416,6 @@ class Parser {
|
||||
case TokenKind::DollarOpenBraceToken:
|
||||
case TokenKind::OpenBraceDollarToken:
|
||||
$expression->children[] = $this->eat(TokenKind::DollarOpenBraceToken, TokenKind::OpenBraceDollarToken);
|
||||
// TODO: Reject ${var->prop} and ${(var->prop)} without rejecting ${var+otherVar}
|
||||
// Currently, this fails to reject ${var->prop} (because `var` has TokenKind::Name instead of StringVarname)
|
||||
if ($this->getCurrentToken()->kind === TokenKind::StringVarname) {
|
||||
$expression->children[] = $this->parseComplexDollarTemplateStringExpression($expression);
|
||||
} else {
|
||||
@@ -1536,6 +1626,9 @@ class Parser {
|
||||
case TokenKind::ProtectedKeyword:
|
||||
case TokenKind::PrivateKeyword:
|
||||
case TokenKind::AttributeToken:
|
||||
|
||||
// dnf types (A&B)|C
|
||||
case TokenKind::OpenParenToken:
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1546,7 +1639,7 @@ class Parser {
|
||||
|
||||
/**
|
||||
* @param string $className (name of subclass of DelimitedList)
|
||||
* @param int $delimiter
|
||||
* @param int|int[] $delimiter
|
||||
* @param callable $isElementStartFn
|
||||
* @param callable $parseElementFn
|
||||
* @param Node $parentNode
|
||||
@@ -1560,7 +1653,7 @@ class Parser {
|
||||
do {
|
||||
if ($isElementStartFn($token)) {
|
||||
$node->addElement($parseElementFn($node));
|
||||
} elseif (!$allowEmptyElements || ($allowEmptyElements && !$this->checkToken($delimiter))) {
|
||||
} elseif (!$allowEmptyElements || ($allowEmptyElements && !$this->checkAnyToken($delimiter))) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1572,7 +1665,6 @@ class Parser {
|
||||
// TODO ERROR CASE - no delimiter, but a param follows
|
||||
} while ($delimiterToken !== null);
|
||||
|
||||
|
||||
$node->parent = $parentNode;
|
||||
if ($node->children === null) {
|
||||
return null;
|
||||
@@ -1772,10 +1864,17 @@ class Parser {
|
||||
return $succeeded;
|
||||
}
|
||||
|
||||
/** @param int $expectedKind */
|
||||
private function checkToken($expectedKind) : bool {
|
||||
return $this->getCurrentToken()->kind === $expectedKind;
|
||||
}
|
||||
|
||||
/** @param int|int[] $expectedKind */
|
||||
private function checkAnyToken($expectedKind) : bool {
|
||||
$kind = $this->getCurrentToken()->kind;
|
||||
return \is_array($expectedKind) ? \in_array($kind, $expectedKind, true) : $kind === $expectedKind;
|
||||
}
|
||||
|
||||
private function parseIfStatement($parentNode) {
|
||||
$ifStatement = new IfStatementNode();
|
||||
$ifStatement->parent = $parentNode;
|
||||
@@ -2114,7 +2213,7 @@ class Parser {
|
||||
}
|
||||
break;
|
||||
case TokenKind::QuestionToken:
|
||||
if ($parentNode instanceof TernaryExpression) {
|
||||
if ($parentNode instanceof TernaryExpression && !isset($parentNode->questionToken)) {
|
||||
// Workaround to parse "a ? b : c ? d : e" as "(a ? b : c) ? d : e"
|
||||
break 2;
|
||||
}
|
||||
@@ -2122,6 +2221,7 @@ class Parser {
|
||||
}
|
||||
|
||||
if ($shouldOperatorTakePrecedenceOverUnary) {
|
||||
/** @var UnaryOpExpression $unaryExpression */
|
||||
$unaryExpression = $leftOperand;
|
||||
$leftOperand = $unaryExpression->operand;
|
||||
}
|
||||
@@ -2143,6 +2243,7 @@ class Parser {
|
||||
|
||||
// Rebuild the unary expression if we deconstructed it earlier.
|
||||
if ($shouldOperatorTakePrecedenceOverUnary) {
|
||||
/** @var UnaryOpExpression $unaryExpression */
|
||||
$leftOperand->parent = $unaryExpression;
|
||||
$unaryExpression->operand = $leftOperand;
|
||||
$leftOperand = $unaryExpression;
|
||||
@@ -2269,6 +2370,14 @@ class Parser {
|
||||
// InstanceOf has other remaining issues, but this heuristic is an improvement for many common cases such as `$x && $y = $z`
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Token|Node $leftOperand
|
||||
* @param Token $operatorToken
|
||||
* @param Token|null $byRefToken
|
||||
* @param Token|Node $rightOperand
|
||||
* @param Node $parentNode
|
||||
* @return BinaryExpression|AssignmentExpression
|
||||
*/
|
||||
private function makeBinaryExpression($leftOperand, $operatorToken, $byRefToken, $rightOperand, $parentNode) {
|
||||
$assignmentExpression = $operatorToken->kind === TokenKind::EqualsToken;
|
||||
if ($assignmentExpression || \array_key_exists($operatorToken->kind, self::KNOWN_ASSIGNMENT_TOKEN_SET)) {
|
||||
@@ -2283,8 +2392,12 @@ class Parser {
|
||||
}
|
||||
$binaryExpression = $assignmentExpression ? new AssignmentExpression() : new BinaryExpression();
|
||||
$binaryExpression->parent = $parentNode;
|
||||
$leftOperand->parent = $binaryExpression;
|
||||
$rightOperand->parent = $binaryExpression;
|
||||
if ($leftOperand instanceof Node) {
|
||||
$leftOperand->parent = $binaryExpression;
|
||||
}
|
||||
if ($rightOperand instanceof Node) {
|
||||
$rightOperand->parent = $binaryExpression;
|
||||
}
|
||||
$binaryExpression->leftOperand = $leftOperand;
|
||||
$binaryExpression->operator = $operatorToken;
|
||||
if ($binaryExpression instanceof AssignmentExpression && isset($byRefToken)) {
|
||||
@@ -2744,6 +2857,30 @@ class Parser {
|
||||
return $unsetStatement;
|
||||
}
|
||||
|
||||
private function parseHaltCompilerStatement($parentNode) {
|
||||
$haltCompilerStatement = new HaltCompilerStatement();
|
||||
$haltCompilerStatement->parent = $parentNode;
|
||||
|
||||
$haltCompilerStatement->haltCompilerKeyword = $this->eat1(TokenKind::HaltCompilerKeyword);
|
||||
$haltCompilerStatement->openParen = $this->eat1(TokenKind::OpenParenToken);
|
||||
$haltCompilerStatement->closeParen = $this->eat1(TokenKind::CloseParenToken);
|
||||
// There is an implicit ';' before the closing php tag.
|
||||
$haltCompilerStatement->semicolonOrCloseTag = $this->eat(TokenKind::SemicolonToken, TokenKind::ScriptSectionEndTag);
|
||||
// token_get_all() will return up to 3 tokens after __halt_compiler regardless of whether they're the right ones.
|
||||
// For invalid php snippets, combine the remaining tokens into InlineHtml
|
||||
$remainingTokens = [];
|
||||
while ($this->token->kind !== TokenKind::EndOfFileToken) {
|
||||
$remainingTokens[] = $this->token;
|
||||
$this->advanceToken();
|
||||
}
|
||||
if ($remainingTokens) {
|
||||
$firstToken = $remainingTokens[0];
|
||||
$lastToken = end($remainingTokens);
|
||||
$haltCompilerStatement->data = new Token(TokenKind::InlineHtml, $firstToken->fullStart, $firstToken->fullStart, $lastToken->fullStart + $lastToken->length - $firstToken->fullStart);
|
||||
}
|
||||
return $haltCompilerStatement;
|
||||
}
|
||||
|
||||
private function parseArrayCreationExpression($parentNode) {
|
||||
$arrayExpression = new ArrayCreationExpression();
|
||||
$arrayExpression->parent = $parentNode;
|
||||
@@ -3207,7 +3344,7 @@ class Parser {
|
||||
if ($classBaseClause->extendsKeyword === null) {
|
||||
return null;
|
||||
}
|
||||
$classBaseClause->baseClass = $this->parseQualifiedName($classBaseClause);
|
||||
$classBaseClause->baseClass = $this->parseQualifiedName($classBaseClause) ?? new MissingToken(TokenKind::QualifiedName, $this->token->fullStart);
|
||||
|
||||
return $classBaseClause;
|
||||
}
|
||||
@@ -3278,6 +3415,8 @@ class Parser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a comma separated qualified name list (e.g. interfaces implemented by a class)
|
||||
*
|
||||
* @param Node $parentNode
|
||||
* @return DelimitedList\QualifiedNameList
|
||||
*/
|
||||
@@ -3291,6 +3430,7 @@ class Parser {
|
||||
}
|
||||
|
||||
private function parseQualifiedNameCatchList($parentNode) {
|
||||
// catch blocks don't support intersection types.
|
||||
$result = $this->parseDelimitedList(
|
||||
DelimitedList\QualifiedNameList::class,
|
||||
TokenKind::BarToken,
|
||||
@@ -3397,12 +3537,18 @@ class Parser {
|
||||
$namespaceDefinition->namespaceKeyword = $this->eat1(TokenKind::NamespaceKeyword);
|
||||
|
||||
if (!$this->checkToken(TokenKind::NamespaceKeyword)) {
|
||||
$namespaceDefinition->name = $this->parseQualifiedName($namespaceDefinition); // TODO only optional with compound statement block
|
||||
$namespaceDefinition->name = $this->parseQualifiedName($namespaceDefinition);
|
||||
}
|
||||
|
||||
$namespaceDefinition->compoundStatementOrSemicolon =
|
||||
$this->checkToken(TokenKind::OpenBraceToken) ?
|
||||
$this->parseCompoundStatement($namespaceDefinition) : $this->eatSemicolonOrAbortStatement();
|
||||
if ($this->checkToken(TokenKind::OpenBraceToken)) {
|
||||
$namespaceDefinition->compoundStatementOrSemicolon = $this->parseCompoundStatement($namespaceDefinition);
|
||||
} else {
|
||||
if (!$namespaceDefinition->name) {
|
||||
// only optional with compound statement block
|
||||
$namespaceDefinition->name = new MissingToken(TokenKind::QualifiedName, $this->token->fullStart);
|
||||
}
|
||||
$namespaceDefinition->compoundStatementOrSemicolon = $this->eatSemicolonOrAbortStatement();
|
||||
}
|
||||
|
||||
return $namespaceDefinition;
|
||||
}
|
||||
@@ -3428,13 +3574,17 @@ class Parser {
|
||||
$namespaceUseClause = new NamespaceUseClause();
|
||||
$namespaceUseClause->parent = $parentNode;
|
||||
$namespaceUseClause->namespaceName = $this->parseQualifiedName($namespaceUseClause);
|
||||
if ($this->checkToken(TokenKind::AsKeyword)) {
|
||||
$namespaceUseClause->namespaceAliasingClause = $this->parseNamespaceAliasingClause($namespaceUseClause);
|
||||
}
|
||||
elseif ($this->checkToken(TokenKind::OpenBraceToken)) {
|
||||
if ($this->checkToken(TokenKind::OpenBraceToken)) {
|
||||
$namespaceUseClause->openBrace = $this->eat1(TokenKind::OpenBraceToken);
|
||||
$namespaceUseClause->groupClauses = $this->parseNamespaceUseGroupClauseList($namespaceUseClause);
|
||||
$namespaceUseClause->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
|
||||
} else {
|
||||
if (!$namespaceUseClause->namespaceName) {
|
||||
$namespaceUseClause->namespaceName = new MissingToken(TokenKind::QualifiedName, $this->token->fullStart);
|
||||
}
|
||||
if ($this->checkToken(TokenKind::AsKeyword)) {
|
||||
$namespaceUseClause->namespaceAliasingClause = $this->parseNamespaceAliasingClause($namespaceUseClause);
|
||||
}
|
||||
}
|
||||
|
||||
return $namespaceUseClause;
|
||||
@@ -3455,7 +3605,7 @@ class Parser {
|
||||
$namespaceUseGroupClause->parent = $parentNode;
|
||||
|
||||
$namespaceUseGroupClause->functionOrConst = $this->eatOptional(TokenKind::FunctionKeyword, TokenKind::ConstKeyword);
|
||||
$namespaceUseGroupClause->namespaceName = $this->parseQualifiedName($namespaceUseGroupClause);
|
||||
$namespaceUseGroupClause->namespaceName = $this->parseQualifiedName($namespaceUseGroupClause) ?? new MissingToken(TokenKind::QualifiedName, $this->token->fullStart);
|
||||
if ($this->checkToken(TokenKind::AsKeyword)) {
|
||||
$namespaceUseGroupClause->namespaceAliasingClause = $this->parseNamespaceAliasingClause($namespaceUseGroupClause);
|
||||
}
|
||||
@@ -3513,6 +3663,7 @@ class Parser {
|
||||
case TokenKind::AbstractKeyword:
|
||||
case TokenKind::FinalKeyword:
|
||||
case TokenKind::ReadonlyKeyword:
|
||||
case TokenKind::ConstKeyword:
|
||||
|
||||
// method-declaration
|
||||
case TokenKind::FunctionKeyword:
|
||||
@@ -3533,6 +3684,9 @@ class Parser {
|
||||
|
||||
$token = $this->getCurrentToken();
|
||||
switch ($token->kind) {
|
||||
case TokenKind::ConstKeyword:
|
||||
return $this->parseClassConstDeclaration($parentNode, $modifiers);
|
||||
|
||||
case TokenKind::FunctionKeyword:
|
||||
return $this->parseMethodDeclaration($parentNode, $modifiers);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user