From 015332a3d3217d24418f5dda837f3316db261945 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Tue, 26 Dec 2023 13:10:49 -0500 Subject: [PATCH 1/4] Support parsing php 8.3 typed class constants https://wiki.php.net/rfc/typed_class_constants#inheritance_and_variance As of php 8.3, class constants can have the same types as parameters (including union, intersection, and DNF types) Start testing with php 8.3 --- .github/workflows/main.yml | 3 +- src/Node/ClassConstDeclaration.php | 5 + src/Parser.php | 16 ++- .../parser/classConstDeclaration1.php.tree | 1 + .../parser/classConstDeclaration10.php.tree | 1 + .../cases/parser/classConstDeclaration11.php | 4 + .../parser/classConstDeclaration11.php.diag | 1 + .../parser/classConstDeclaration11.php.tree | 109 ++++++++++++++++ .../cases/parser/classConstDeclaration12.php | 4 + .../parser/classConstDeclaration12.php.diag | 26 ++++ .../parser/classConstDeclaration12.php.tree | 120 ++++++++++++++++++ .../parser/classConstDeclaration2.php.tree | 1 + .../parser/classConstDeclaration4.php.tree | 1 + .../parser/classConstDeclaration5.php.tree | 1 + .../parser/classConstDeclaration7.php.tree | 1 + .../parser/classConstDeclaration8.php.tree | 1 + .../parser/classConstDeclaration9.php.tree | 1 + .../parser/interfaceDeclaration15.php.tree | 1 + .../parser/interfaceDeclaration16.php.tree | 1 + tests/cases/parser/traits26.php.tree | 2 + tests/cases/parser80/attributes4.php.tree | 1 + tests/cases/parser81/enums2.php.tree | 1 + 22 files changed, 296 insertions(+), 6 deletions(-) create mode 100644 tests/cases/parser/classConstDeclaration11.php create mode 100644 tests/cases/parser/classConstDeclaration11.php.diag create mode 100644 tests/cases/parser/classConstDeclaration11.php.tree create mode 100644 tests/cases/parser/classConstDeclaration12.php create mode 100644 tests/cases/parser/classConstDeclaration12.php.diag create mode 100644 tests/cases/parser/classConstDeclaration12.php.tree diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index edc2dd59..7283d2ce 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,7 +31,8 @@ jobs: - PHP_VERSION: '7.4' - PHP_VERSION: '8.0' - PHP_VERSION: '8.1' - - PHP_VERSION: '8.2.0beta2' + - PHP_VERSION: '8.2' + - PHP_VERSION: '8.3' # Steps represent a sequence of tasks that will be executed as part of the job steps: diff --git a/src/Node/ClassConstDeclaration.php b/src/Node/ClassConstDeclaration.php index 2807bc23..bbbe0c69 100644 --- a/src/Node/ClassConstDeclaration.php +++ b/src/Node/ClassConstDeclaration.php @@ -9,6 +9,7 @@ use Microsoft\PhpParser\ModifiedTypeInterface; use Microsoft\PhpParser\ModifiedTypeTrait; use Microsoft\PhpParser\Node; +use Microsoft\PhpParser\Node\DelimitedList\QualifiedNameList; use Microsoft\PhpParser\Token; class ClassConstDeclaration extends Node implements ModifiedTypeInterface { @@ -23,6 +24,9 @@ class ClassConstDeclaration extends Node implements ModifiedTypeInterface { /** @var Token */ public $constKeyword; + /** @var QualifiedNameList|null */ + public $typeDeclarationList; + /** @var DelimitedList\ConstElementList */ public $constElements; @@ -33,6 +37,7 @@ class ClassConstDeclaration extends Node implements ModifiedTypeInterface { 'attributes', 'modifiers', 'constKeyword', + 'typeDeclarationList', 'constElements', 'semicolon' ]; diff --git a/src/Parser.php b/src/Parser.php index 100c6c0c..47096867 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1417,7 +1417,7 @@ private function parseStringLiteralExpression2($parentNode): StringLiteral { case TokenKind::DollarOpenBraceToken: case TokenKind::OpenBraceDollarToken: $expression->children[] = $this->eat(TokenKind::DollarOpenBraceToken, TokenKind::OpenBraceDollarToken); - /** + /** * @phpstan-ignore-next-line "Strict comparison using * === between 403|404 and 408 will always evaluate to * false" is wrong because those tokens were eaten above @@ -2802,10 +2802,6 @@ private function parseListIntrinsicExpression($parentNode) { return $listExpression; } - private function isArrayElementStart($token) { - return ($this->isArrayElementStartFn())($token); - } - private function isArrayElementStartFn() { return function ($token) { return $token->kind === TokenKind::AmpersandToken || $token->kind === TokenKind::DotDotDotToken || $this->isExpressionStart($token); @@ -3370,6 +3366,16 @@ private function parseClassConstDeclaration($parentNode, $modifiers) { $classConstDeclaration->modifiers = $modifiers; $classConstDeclaration->constKeyword = $this->eat1(TokenKind::ConstKeyword); + // Handle class constant declarations such as `const X|Y Z = 123;` or `const X = 123;`. + // This is similar to lookahead(). + $startPos = $this->lexer->getCurrentPosition(); + $startToken = $this->token; + $classConstDeclaration->typeDeclarationList = $this->tryParseParameterTypeDeclarationList($classConstDeclaration); + if (in_array($this->token->kind, [TokenKind::EqualsToken, TokenKind::CommaToken, TokenKind::SemicolonToken]) && $this->lexer->getCurrentPosition() <= $startPos + 1) { + $classConstDeclaration->typeDeclarationList = null; + $this->lexer->setCurrentPosition($startPos); + $this->token = $startToken; + } $classConstDeclaration->constElements = $this->parseConstElements($classConstDeclaration); $classConstDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken); diff --git a/tests/cases/parser/classConstDeclaration1.php.tree b/tests/cases/parser/classConstDeclaration1.php.tree index 664bc14f..1d2482b1 100644 --- a/tests/cases/parser/classConstDeclaration1.php.tree +++ b/tests/cases/parser/classConstDeclaration1.php.tree @@ -41,6 +41,7 @@ "kind": "ConstKeyword", "textLength": 5 }, + "typeDeclarationList": null, "constElements": { "ConstElementList": { "children": [ diff --git a/tests/cases/parser/classConstDeclaration10.php.tree b/tests/cases/parser/classConstDeclaration10.php.tree index 93a0dfa2..8e6057df 100644 --- a/tests/cases/parser/classConstDeclaration10.php.tree +++ b/tests/cases/parser/classConstDeclaration10.php.tree @@ -46,6 +46,7 @@ "kind": "ConstKeyword", "textLength": 5 }, + "typeDeclarationList": null, "constElements": { "ConstElementList": { "children": [ diff --git a/tests/cases/parser/classConstDeclaration11.php b/tests/cases/parser/classConstDeclaration11.php new file mode 100644 index 00000000..26a66806 --- /dev/null +++ b/tests/cases/parser/classConstDeclaration11.php @@ -0,0 +1,4 @@ + Date: Tue, 26 Dec 2023 14:24:21 -0500 Subject: [PATCH 2/4] Skip keyword5 - lexing of T_YIELD_FROM in php 8.3 changed --- tests/skipped.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/skipped.json b/tests/skipped.json index a7673fd1..4ad1b974 100644 --- a/tests/skipped.json +++ b/tests/skipped.json @@ -18,6 +18,7 @@ "globalVariableDeclarationStatement3.php", "interfaceDeclaration17.php", "interfaceDeclaration6.php", + "keyword5.php.tokens", "listExpression17.php", "listExpression27.php", "listExpression28.php", @@ -62,4 +63,4 @@ "core_predefined_constants2.php", "void_parameter.php" -] \ No newline at end of file +] From 4070395268accac4c5284f327c25d6b105a8fc56 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Wed, 3 Jan 2024 10:58:01 -0500 Subject: [PATCH 3/4] Parse start of FQ namespaced class constant type --- src/Parser.php | 3 +- .../cases/parser/classConstDeclaration13.php | 4 + .../parser/classConstDeclaration13.php.diag | 8 ++ .../parser/classConstDeclaration13.php.tree | 83 +++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 tests/cases/parser/classConstDeclaration13.php create mode 100644 tests/cases/parser/classConstDeclaration13.php.diag create mode 100644 tests/cases/parser/classConstDeclaration13.php.tree diff --git a/src/Parser.php b/src/Parser.php index 47096867..bf4f900c 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -3371,7 +3371,8 @@ private function parseClassConstDeclaration($parentNode, $modifiers) { $startPos = $this->lexer->getCurrentPosition(); $startToken = $this->token; $classConstDeclaration->typeDeclarationList = $this->tryParseParameterTypeDeclarationList($classConstDeclaration); - if (in_array($this->token->kind, [TokenKind::EqualsToken, TokenKind::CommaToken, TokenKind::SemicolonToken]) && $this->lexer->getCurrentPosition() <= $startPos + 1) { + if ($startToken->kind !== TokenKind::BackslashToken && + in_array($this->token->kind, [TokenKind::EqualsToken, TokenKind::CommaToken, TokenKind::SemicolonToken]) && $this->lexer->getCurrentPosition() <= $startPos + 1) { $classConstDeclaration->typeDeclarationList = null; $this->lexer->setCurrentPosition($startPos); $this->token = $startToken; diff --git a/tests/cases/parser/classConstDeclaration13.php b/tests/cases/parser/classConstDeclaration13.php new file mode 100644 index 00000000..2b6d813f --- /dev/null +++ b/tests/cases/parser/classConstDeclaration13.php @@ -0,0 +1,4 @@ + Date: Wed, 3 Jan 2024 11:07:06 -0500 Subject: [PATCH 4/4] Add test of parsing end of file after class constant Assume most class constants won't add a type and parse the name if ambiguous. --- src/Parser.php | 3 +- .../cases/parser/classConstDeclaration14.php | 3 + .../parser/classConstDeclaration14.php.diag | 26 ++++++ .../parser/classConstDeclaration14.php.tree | 92 +++++++++++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 tests/cases/parser/classConstDeclaration14.php create mode 100644 tests/cases/parser/classConstDeclaration14.php.diag create mode 100644 tests/cases/parser/classConstDeclaration14.php.tree diff --git a/src/Parser.php b/src/Parser.php index bf4f900c..8185ce22 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -3372,7 +3372,8 @@ private function parseClassConstDeclaration($parentNode, $modifiers) { $startToken = $this->token; $classConstDeclaration->typeDeclarationList = $this->tryParseParameterTypeDeclarationList($classConstDeclaration); if ($startToken->kind !== TokenKind::BackslashToken && - in_array($this->token->kind, [TokenKind::EqualsToken, TokenKind::CommaToken, TokenKind::SemicolonToken]) && $this->lexer->getCurrentPosition() <= $startPos + 1) { + in_array($this->token->kind, [TokenKind::EqualsToken, TokenKind::CommaToken, TokenKind::SemicolonToken, TokenKind::EndOfFileToken]) && + $this->lexer->getCurrentPosition() <= $startPos + 1) { $classConstDeclaration->typeDeclarationList = null; $this->lexer->setCurrentPosition($startPos); $this->token = $startToken; diff --git a/tests/cases/parser/classConstDeclaration14.php b/tests/cases/parser/classConstDeclaration14.php new file mode 100644 index 00000000..c8b4c49d --- /dev/null +++ b/tests/cases/parser/classConstDeclaration14.php @@ -0,0 +1,3 @@ +