From 0dac21447d916ee3d1c6fc2736b93d5ef0efac57 Mon Sep 17 00:00:00 2001
From: Daniel Leech <dan.t.leech@gmail.com>
Date: Fri, 11 Nov 2022 23:45:54 +0100
Subject: [PATCH 01/11] Tolerate open string

---
 src/Parser.php | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/Parser.php b/src/Parser.php
index c5a4af2d..7813142d 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -1223,6 +1223,7 @@ private function isExpressionStartFn() {
                 case TokenKind::IntegerLiteralToken:
 
                 case TokenKind::StringLiteralToken:
+                case TokenKind::EncapsedAndWhitespace:
 
                 case TokenKind::SingleQuoteToken:
                 case TokenKind::DoubleQuoteToken:
@@ -1316,6 +1317,7 @@ private function parsePrimaryExpression($parentNode) {
                 return $this->parseNumericLiteralExpression($parentNode);
 
             case TokenKind::StringLiteralToken:
+            case TokenKind::EncapsedAndWhitespace:
                 return $this->parseStringLiteralExpression($parentNode);
 
             case TokenKind::DoubleQuoteToken:
@@ -2686,7 +2688,7 @@ private function parseDeclareDirectiveFn() {
                     TokenKind::InvalidOctalLiteralToken,
                     TokenKind::InvalidHexadecimalLiteral,
                     TokenKind::InvalidBinaryLiteral,
-                    TokenKind::StringLiteralToken
+                    TokenKind::StringLiteralToken,
                 ); // TODO simplify
 
             return $declareDirective;

From ca6029488886b8a9c7e85a41c624a45afc3215e5 Mon Sep 17 00:00:00 2001
From: Daniel <daniel@dantleech.com>
Date: Thu, 20 Apr 2023 21:35:58 +0100
Subject: [PATCH 02/11] Support enum interfaces

---
 src/Node/EnumInterfaceClause.php       | 23 ++++++++
 src/Node/Statement/EnumDeclaration.php |  5 ++
 src/Parser.php                         | 19 +++++-
 tests/cases/parser81/enums1.php.tree   |  1 +
 tests/cases/parser81/enums2.php.tree   |  1 +
 tests/cases/parser81/enums3.php.tree   |  1 +
 tests/cases/parser81/enums4.php.tree   |  1 +
 tests/cases/parser81/enums5.php.tree   |  1 +
 tests/cases/parser81/enums6.php.tree   |  1 +
 tests/cases/parser81/enums7.php        |  4 ++
 tests/cases/parser81/enums7.php.diag   |  1 +
 tests/cases/parser81/enums7.php.tree   | 80 ++++++++++++++++++++++++++
 12 files changed, 137 insertions(+), 1 deletion(-)
 create mode 100644 src/Node/EnumInterfaceClause.php
 create mode 100644 tests/cases/parser81/enums7.php
 create mode 100644 tests/cases/parser81/enums7.php.diag
 create mode 100644 tests/cases/parser81/enums7.php.tree

diff --git a/src/Node/EnumInterfaceClause.php b/src/Node/EnumInterfaceClause.php
new file mode 100644
index 00000000..e50c8a6a
--- /dev/null
+++ b/src/Node/EnumInterfaceClause.php
@@ -0,0 +1,23 @@
+<?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 EnumInterfaceClause extends Node {
+    /** @var Token */
+    public $implementsKeyword;
+
+    /** @var DelimitedList\QualifiedNameList|null */
+    public $interfaceNameList;
+
+    const CHILD_NAMES = [
+        'implementsKeyword',
+        'interfaceNameList'
+    ];
+}
diff --git a/src/Node/Statement/EnumDeclaration.php b/src/Node/Statement/EnumDeclaration.php
index d2154b77..8f793e50 100644
--- a/src/Node/Statement/EnumDeclaration.php
+++ b/src/Node/Statement/EnumDeclaration.php
@@ -10,6 +10,7 @@
 use Microsoft\PhpParser\NamespacedNameInterface;
 use Microsoft\PhpParser\NamespacedNameTrait;
 use Microsoft\PhpParser\Node\AttributeGroup;
+use Microsoft\PhpParser\Node\EnumInterfaceClause;
 use Microsoft\PhpParser\Node\StatementNode;
 use Microsoft\PhpParser\Node\EnumMembers;
 use Microsoft\PhpParser\Token;
@@ -23,6 +24,9 @@ class EnumDeclaration extends StatementNode implements NamespacedNameInterface,
     /** @var Token */
     public $enumKeyword;
 
+    /** @var EnumInterfaceClause|null */
+    public $enumInterfaceClause;
+
     /** @var Token */
     public $name;
 
@@ -41,6 +45,7 @@ class EnumDeclaration extends StatementNode implements NamespacedNameInterface,
         'name',
         'colonToken',
         'enumType',
+        'enumInterfaceClause',
         'enumMembers',
     ];
 
diff --git a/src/Parser.php b/src/Parser.php
index 7813142d..712e7ac1 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -18,6 +18,7 @@
 use Microsoft\PhpParser\Node\ClassMembersNode;
 use Microsoft\PhpParser\Node\ConstElement;
 use Microsoft\PhpParser\Node\EnumCaseDeclaration;
+use Microsoft\PhpParser\Node\EnumInterfaceClause;
 use Microsoft\PhpParser\Node\EnumMembers;
 use Microsoft\PhpParser\Node\Expression;
 use Microsoft\PhpParser\Node\Expression\{
@@ -3386,6 +3387,22 @@ private function parseEnumCaseDeclaration($parentNode) {
         return $enumCaseDeclaration;
     }
 
+    private function parseEnumInterfaceClause(EnumDeclaration $enumDeclaration): ?EnumInterfaceClause {
+        $enumInterfaceClause = new EnumInterfaceClause();
+        $enumInterfaceClause->parent = $enumDeclaration;
+        $enumInterfaceClause->implementsKeyword = $this->eatOptional1(TokenKind::ImplementsKeyword);
+
+        if ($enumInterfaceClause->implementsKeyword === null) {
+            return null;
+        }
+
+        $enumInterfaceClause->interfaceNameList =
+            $this->parseQualifiedNameList($enumInterfaceClause);
+        return $enumInterfaceClause;
+    }
+
+
+
     /**
      * @param Node $parentNode
      * @param Token[] $modifiers
@@ -3724,7 +3741,6 @@ private function parseTraitElementFn() {
     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);
@@ -3732,6 +3748,7 @@ private function parseEnumDeclaration($parentNode) {
             $enumDeclaration->enumType = $this->tryParseParameterTypeDeclaration($enumDeclaration)
                 ?: new MissingToken(TokenKind::EnumType, $this->token->fullStart);
         }
+        $enumDeclaration->enumInterfaceClause = $this->parseEnumInterfaceClause($enumDeclaration);
 
         $enumDeclaration->enumMembers = $this->parseEnumMembers($enumDeclaration);
 
diff --git a/tests/cases/parser81/enums1.php.tree b/tests/cases/parser81/enums1.php.tree
index 36f22d63..427e862c 100644
--- a/tests/cases/parser81/enums1.php.tree
+++ b/tests/cases/parser81/enums1.php.tree
@@ -24,6 +24,7 @@
                     },
                     "colonToken": null,
                     "enumType": null,
+                    "enumInterfaceClause": null,
                     "enumMembers": {
                         "EnumMembers": {
                             "openBrace": {
diff --git a/tests/cases/parser81/enums2.php.tree b/tests/cases/parser81/enums2.php.tree
index acb453dd..7ecf67d8 100644
--- a/tests/cases/parser81/enums2.php.tree
+++ b/tests/cases/parser81/enums2.php.tree
@@ -30,6 +30,7 @@
                         "kind": "IntReservedWord",
                         "textLength": 3
                     },
+                    "enumInterfaceClause": null,
                     "enumMembers": {
                         "EnumMembers": {
                             "openBrace": {
diff --git a/tests/cases/parser81/enums3.php.tree b/tests/cases/parser81/enums3.php.tree
index 0d6106c3..c6acbe9a 100644
--- a/tests/cases/parser81/enums3.php.tree
+++ b/tests/cases/parser81/enums3.php.tree
@@ -62,6 +62,7 @@
                     },
                     "colonToken": null,
                     "enumType": null,
+                    "enumInterfaceClause": null,
                     "enumMembers": {
                         "EnumMembers": {
                             "openBrace": {
diff --git a/tests/cases/parser81/enums4.php.tree b/tests/cases/parser81/enums4.php.tree
index 682e41eb..f150be7e 100644
--- a/tests/cases/parser81/enums4.php.tree
+++ b/tests/cases/parser81/enums4.php.tree
@@ -30,6 +30,7 @@
                         "kind": "StringReservedWord",
                         "textLength": 6
                     },
+                    "enumInterfaceClause": null,
                     "enumMembers": {
                         "EnumMembers": {
                             "openBrace": {
diff --git a/tests/cases/parser81/enums5.php.tree b/tests/cases/parser81/enums5.php.tree
index 881f69e5..f75a275e 100644
--- a/tests/cases/parser81/enums5.php.tree
+++ b/tests/cases/parser81/enums5.php.tree
@@ -31,6 +31,7 @@
                         "kind": "EnumType",
                         "textLength": 0
                     },
+                    "enumInterfaceClause": null,
                     "enumMembers": {
                         "EnumMembers": {
                             "openBrace": {
diff --git a/tests/cases/parser81/enums6.php.tree b/tests/cases/parser81/enums6.php.tree
index 6fc145ab..65271765 100644
--- a/tests/cases/parser81/enums6.php.tree
+++ b/tests/cases/parser81/enums6.php.tree
@@ -30,6 +30,7 @@
                         "kind": "IntReservedWord",
                         "textLength": 3
                     },
+                    "enumInterfaceClause": null,
                     "enumMembers": {
                         "EnumMembers": {
                             "openBrace": {
diff --git a/tests/cases/parser81/enums7.php b/tests/cases/parser81/enums7.php
new file mode 100644
index 00000000..e95aee43
--- /dev/null
+++ b/tests/cases/parser81/enums7.php
@@ -0,0 +1,4 @@
+<?php
+enum Foo: int implements Foo
+{
+}
diff --git a/tests/cases/parser81/enums7.php.diag b/tests/cases/parser81/enums7.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser81/enums7.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser81/enums7.php.tree b/tests/cases/parser81/enums7.php.tree
new file mode 100644
index 00000000..dbea9669
--- /dev/null
+++ b/tests/cases/parser81/enums7.php.tree
@@ -0,0 +1,80 @@
+{
+    "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
+                    },
+                    "enumInterfaceClause": {
+                        "EnumInterfaceClause": {
+                            "implementsKeyword": {
+                                "kind": "ImplementsKeyword",
+                                "textLength": 10
+                            },
+                            "interfaceNameList": {
+                                "QualifiedNameList": {
+                                    "children": [
+                                        {
+                                            "QualifiedName": {
+                                                "globalSpecifier": null,
+                                                "relativeSpecifier": null,
+                                                "nameParts": [
+                                                    {
+                                                        "kind": "Name",
+                                                        "textLength": 3
+                                                    }
+                                                ]
+                                            }
+                                        }
+                                    ]
+                                }
+                            }
+                        }
+                    },
+                    "enumMembers": {
+                        "EnumMembers": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "enumMemberDeclarations": [],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file

From 7d843d2460481c251d9c33818f659b66f6b99482 Mon Sep 17 00:00:00 2001
From: Daniel Leech <daniel@dantleech.com>
Date: Sat, 6 Jan 2024 17:46:21 +0000
Subject: [PATCH 03/11] Fix conflicts

---
 src/Parser.php | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/src/Parser.php b/src/Parser.php
index 9d78a3fa..7a0957c4 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -3393,22 +3393,6 @@ private function parseEnumCaseDeclaration($parentNode) {
         return $enumCaseDeclaration;
     }
 
-    private function parseEnumInterfaceClause(EnumDeclaration $enumDeclaration): ?EnumInterfaceClause {
-        $enumInterfaceClause = new EnumInterfaceClause();
-        $enumInterfaceClause->parent = $enumDeclaration;
-        $enumInterfaceClause->implementsKeyword = $this->eatOptional1(TokenKind::ImplementsKeyword);
-
-        if ($enumInterfaceClause->implementsKeyword === null) {
-            return null;
-        }
-
-        $enumInterfaceClause->interfaceNameList =
-            $this->parseQualifiedNameList($enumInterfaceClause);
-        return $enumInterfaceClause;
-    }
-
-
-
     /**
      * @param Node $parentNode
      * @param Token[] $modifiers
@@ -3754,7 +3738,6 @@ private function parseEnumDeclaration($parentNode) {
             $enumDeclaration->enumType = $this->tryParseParameterTypeDeclaration($enumDeclaration)
                 ?: new MissingToken(TokenKind::EnumType, $this->token->fullStart);
         }
-        $enumDeclaration->enumInterfaceClause = $this->parseEnumInterfaceClause($enumDeclaration);
 
         $enumDeclaration->enumInterfaceClause = $this->parseEnumInterfaceClause($enumDeclaration);
         $enumDeclaration->enumMembers = $this->parseEnumMembers($enumDeclaration);

From bdc7009126ed73637e9e3fe25b9b9018f0228179 Mon Sep 17 00:00:00 2001
From: Daniel Leech <daniel@dantleech.com>
Date: Sat, 6 Jan 2024 17:54:12 +0000
Subject: [PATCH 04/11] bugfix: Support attributes on readonly classes

---
 src/Parser.php                                |  2 +-
 .../parser81/readonly_class_attribute.php     |  2 +
 .../readonly_class_attribute.php.diag         |  1 +
 .../readonly_class_attribute.php.tree         | 91 +++++++++++++++++++
 4 files changed, 95 insertions(+), 1 deletion(-)
 create mode 100644 tests/cases/parser81/readonly_class_attribute.php
 create mode 100644 tests/cases/parser81/readonly_class_attribute.php.diag
 create mode 100644 tests/cases/parser81/readonly_class_attribute.php.tree

diff --git a/src/Parser.php b/src/Parser.php
index 7a0957c4..576889d1 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -755,7 +755,7 @@ private function parseAttributeStatement($parentNode) {
             $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, TokenKind::EnumKeyword], true) ||
+            if (in_array($this->token->kind, [TokenKind::ReadonlyKeyword, 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 {
diff --git a/tests/cases/parser81/readonly_class_attribute.php b/tests/cases/parser81/readonly_class_attribute.php
new file mode 100644
index 00000000..513aa8d9
--- /dev/null
+++ b/tests/cases/parser81/readonly_class_attribute.php
@@ -0,0 +1,2 @@
+<?php
+#[Foobar]readonly class A {}
diff --git a/tests/cases/parser81/readonly_class_attribute.php.diag b/tests/cases/parser81/readonly_class_attribute.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser81/readonly_class_attribute.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser81/readonly_class_attribute.php.tree b/tests/cases/parser81/readonly_class_attribute.php.tree
new file mode 100644
index 00000000..4e9e4784
--- /dev/null
+++ b/tests/cases/parser81/readonly_class_attribute.php.tree
@@ -0,0 +1,91 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "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
+                                }
+                            }
+                        }
+                    ],
+                    "abstractOrFinalModifier": {
+                        "kind": "ReadonlyKeyword",
+                        "textLength": 8
+                    },
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 1
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file

From d31c0857862e4fed4f7a364dfe19c8c666ee1389 Mon Sep 17 00:00:00 2001
From: Tyson Andre <tysonandre775@hotmail.com>
Date: Tue, 26 Dec 2023 13:10:49 -0500
Subject: [PATCH 05/11] 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 4e55dcb6..58615661 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 @@
+<?php
+class A {
+    const int|string a = 3;
+}
diff --git a/tests/cases/parser/classConstDeclaration11.php.diag b/tests/cases/parser/classConstDeclaration11.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser/classConstDeclaration11.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser/classConstDeclaration11.php.tree b/tests/cases/parser/classConstDeclaration11.php.tree
new file mode 100644
index 00000000..ca62f46f
--- /dev/null
+++ b/tests/cases/parser/classConstDeclaration11.php.tree
@@ -0,0 +1,109 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 1
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "ClassConstDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [],
+                                        "constKeyword": {
+                                            "kind": "ConstKeyword",
+                                            "textLength": 5
+                                        },
+                                        "typeDeclarationList": {
+                                            "QualifiedNameList": {
+                                                "children": [
+                                                    {
+                                                        "kind": "IntReservedWord",
+                                                        "textLength": 3
+                                                    },
+                                                    {
+                                                        "kind": "BarToken",
+                                                        "textLength": 1
+                                                    },
+                                                    {
+                                                        "kind": "StringReservedWord",
+                                                        "textLength": 6
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "constElements": {
+                                            "ConstElementList": {
+                                                "children": [
+                                                    {
+                                                        "ConstElement": {
+                                                            "name": {
+                                                                "kind": "Name",
+                                                                "textLength": 1
+                                                            },
+                                                            "equalsToken": {
+                                                                "kind": "EqualsToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "assignment": {
+                                                                "NumericLiteral": {
+                                                                    "children": {
+                                                                        "kind": "IntegerLiteralToken",
+                                                                        "textLength": 1
+                                                                    }
+                                                                }
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "semicolon": {
+                                            "kind": "SemicolonToken",
+                                            "textLength": 1
+                                        }
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/classConstDeclaration12.php b/tests/cases/parser/classConstDeclaration12.php
new file mode 100644
index 00000000..d2482393
--- /dev/null
+++ b/tests/cases/parser/classConstDeclaration12.php
@@ -0,0 +1,4 @@
+<?php
+class A {
+    const int| a = 3;
+}
diff --git a/tests/cases/parser/classConstDeclaration12.php.diag b/tests/cases/parser/classConstDeclaration12.php.diag
new file mode 100644
index 00000000..cfdad838
--- /dev/null
+++ b/tests/cases/parser/classConstDeclaration12.php.diag
@@ -0,0 +1,26 @@
+[
+    {
+        "kind": 0,
+        "message": "';' expected.",
+        "start": 32,
+        "length": 0
+    },
+    {
+        "kind": 0,
+        "message": "Unexpected '='",
+        "start": 33,
+        "length": 1
+    },
+    {
+        "kind": 0,
+        "message": "'}' expected.",
+        "start": 34,
+        "length": 0
+    },
+    {
+        "kind": 0,
+        "message": "Unexpected '}'",
+        "start": 38,
+        "length": 1
+    }
+]
\ No newline at end of file
diff --git a/tests/cases/parser/classConstDeclaration12.php.tree b/tests/cases/parser/classConstDeclaration12.php.tree
new file mode 100644
index 00000000..1f149a08
--- /dev/null
+++ b/tests/cases/parser/classConstDeclaration12.php.tree
@@ -0,0 +1,120 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 1
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "ClassConstDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [],
+                                        "constKeyword": {
+                                            "kind": "ConstKeyword",
+                                            "textLength": 5
+                                        },
+                                        "typeDeclarationList": {
+                                            "QualifiedNameList": {
+                                                "children": [
+                                                    {
+                                                        "kind": "IntReservedWord",
+                                                        "textLength": 3
+                                                    },
+                                                    {
+                                                        "kind": "BarToken",
+                                                        "textLength": 1
+                                                    },
+                                                    {
+                                                        "QualifiedName": {
+                                                            "globalSpecifier": null,
+                                                            "relativeSpecifier": null,
+                                                            "nameParts": [
+                                                                {
+                                                                    "kind": "Name",
+                                                                    "textLength": 1
+                                                                }
+                                                            ]
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "constElements": null,
+                                        "semicolon": {
+                                            "error": "MissingToken",
+                                            "kind": "SemicolonToken",
+                                            "textLength": 0
+                                        }
+                                    }
+                                },
+                                {
+                                    "error": "SkippedToken",
+                                    "kind": "EqualsToken",
+                                    "textLength": 1
+                                }
+                            ],
+                            "closeBrace": {
+                                "error": "MissingToken",
+                                "kind": "CloseBraceToken",
+                                "textLength": 0
+                            }
+                        }
+                    }
+                }
+            },
+            {
+                "ExpressionStatement": {
+                    "expression": {
+                        "NumericLiteral": {
+                            "children": {
+                                "kind": "IntegerLiteralToken",
+                                "textLength": 1
+                            }
+                        }
+                    },
+                    "semicolon": {
+                        "kind": "SemicolonToken",
+                        "textLength": 1
+                    }
+                }
+            },
+            {
+                "error": "SkippedToken",
+                "kind": "CloseBraceToken",
+                "textLength": 1
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/classConstDeclaration2.php.tree b/tests/cases/parser/classConstDeclaration2.php.tree
index d3e6b491..1a164e07 100644
--- a/tests/cases/parser/classConstDeclaration2.php.tree
+++ b/tests/cases/parser/classConstDeclaration2.php.tree
@@ -41,6 +41,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser/classConstDeclaration4.php.tree b/tests/cases/parser/classConstDeclaration4.php.tree
index 1a372e12..d2573484 100644
--- a/tests/cases/parser/classConstDeclaration4.php.tree
+++ b/tests/cases/parser/classConstDeclaration4.php.tree
@@ -46,6 +46,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser/classConstDeclaration5.php.tree b/tests/cases/parser/classConstDeclaration5.php.tree
index 0c01541d..3fd0b0ad 100644
--- a/tests/cases/parser/classConstDeclaration5.php.tree
+++ b/tests/cases/parser/classConstDeclaration5.php.tree
@@ -50,6 +50,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser/classConstDeclaration7.php.tree b/tests/cases/parser/classConstDeclaration7.php.tree
index 15113d99..866ed05a 100644
--- a/tests/cases/parser/classConstDeclaration7.php.tree
+++ b/tests/cases/parser/classConstDeclaration7.php.tree
@@ -41,6 +41,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser/classConstDeclaration8.php.tree b/tests/cases/parser/classConstDeclaration8.php.tree
index f8bbb9dd..49d95e72 100644
--- a/tests/cases/parser/classConstDeclaration8.php.tree
+++ b/tests/cases/parser/classConstDeclaration8.php.tree
@@ -41,6 +41,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser/classConstDeclaration9.php.tree b/tests/cases/parser/classConstDeclaration9.php.tree
index cb9cf049..128a78ca 100644
--- a/tests/cases/parser/classConstDeclaration9.php.tree
+++ b/tests/cases/parser/classConstDeclaration9.php.tree
@@ -46,6 +46,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser/interfaceDeclaration15.php.tree b/tests/cases/parser/interfaceDeclaration15.php.tree
index 71c49c78..54197c1f 100644
--- a/tests/cases/parser/interfaceDeclaration15.php.tree
+++ b/tests/cases/parser/interfaceDeclaration15.php.tree
@@ -43,6 +43,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser/interfaceDeclaration16.php.tree b/tests/cases/parser/interfaceDeclaration16.php.tree
index 436abcdf..c3704d6f 100644
--- a/tests/cases/parser/interfaceDeclaration16.php.tree
+++ b/tests/cases/parser/interfaceDeclaration16.php.tree
@@ -38,6 +38,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser/traits26.php.tree b/tests/cases/parser/traits26.php.tree
index 70cc76dc..2745cba7 100644
--- a/tests/cases/parser/traits26.php.tree
+++ b/tests/cases/parser/traits26.php.tree
@@ -37,6 +37,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
@@ -82,6 +83,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser80/attributes4.php.tree b/tests/cases/parser80/attributes4.php.tree
index 6fae34ba..6911f240 100644
--- a/tests/cases/parser80/attributes4.php.tree
+++ b/tests/cases/parser80/attributes4.php.tree
@@ -243,6 +243,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [
diff --git a/tests/cases/parser81/enums2.php.tree b/tests/cases/parser81/enums2.php.tree
index 7ecf67d8..a5966bbd 100644
--- a/tests/cases/parser81/enums2.php.tree
+++ b/tests/cases/parser81/enums2.php.tree
@@ -46,6 +46,7 @@
                                             "kind": "ConstKeyword",
                                             "textLength": 5
                                         },
+                                        "typeDeclarationList": null,
                                         "constElements": {
                                             "ConstElementList": {
                                                 "children": [

From fd4d24176f13f8fb9e0f77ffc573e7a7e985d205 Mon Sep 17 00:00:00 2001
From: Daniel Leech <daniel@dantleech.com>
Date: Mon, 13 Jan 2025 20:03:36 +0000
Subject: [PATCH 06/11] PHP8.4: Support new without parenthesis

---
 src/Parser.php                                 | 18 +++++++++++++++++-
 .../cases/parser84/new-without-parenthesis.php |  3 +++
 .../parser84/new-without-parenthesis.php.diag  |  3 +++
 .../parser84/new-without-parenthesis.php.tree  |  3 +++
 4 files changed, 26 insertions(+), 1 deletion(-)
 create mode 100644 tests/cases/parser84/new-without-parenthesis.php
 create mode 100644 tests/cases/parser84/new-without-parenthesis.php.diag
 create mode 100644 tests/cases/parser84/new-without-parenthesis.php.tree

diff --git a/src/Parser.php b/src/Parser.php
index 93f4ffa4..1e24b007 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -3045,7 +3045,8 @@ private function parsePostfixExpressionRest($expression, $allowUpdateExpression
             $expression instanceof SubscriptExpression ||
             $expression instanceof ScopedPropertyAccessExpression ||
             $expression instanceof StringLiteral ||
-            $expression instanceof ArrayCreationExpression
+            $expression instanceof ArrayCreationExpression ||
+            $expression instanceof ObjectCreationExpression
         )) {
             return $expression;
         }
@@ -3199,6 +3200,16 @@ private function parseSubscriptExpression($expression) : SubscriptExpression {
         return $subscriptExpression;
     }
 
+    private function parseNewObjectNoParenthesisMemberAccessExpression($expression):MemberAccessExpression {
+        $memberAccessExpression = new MemberAccessExpression();
+        $memberAccessExpression->parent = $expression;
+
+        $memberAccessExpression->dereferencableExpression = $expression;
+        $memberAccessExpression->arrowToken = $this->eat(TokenKind::ArrowToken, TokenKind::QuestionArrowToken);
+        $memberAccessExpression->memberName = $this->parseMemberName($memberAccessExpression);
+
+        return $memberAccessExpression;
+    }
     private function parseMemberAccessExpression($expression):MemberAccessExpression {
         $memberAccessExpression = new MemberAccessExpression();
         $memberAccessExpression->parent = $expression->parent;
@@ -3287,6 +3298,11 @@ private function parseObjectCreationExpression($parentNode) {
             $objectCreationExpression->classMembers = $this->parseClassMembers($objectCreationExpression);
         }
 
+        // PHP8.4 new with no parenthesis
+        if ($this->getCurrentToken()->kind === TokenKind::ArrowToken) {
+            return $this->parsePostfixExpressionRest($objectCreationExpression);
+        }
+
         return $objectCreationExpression;
     }
 
diff --git a/tests/cases/parser84/new-without-parenthesis.php b/tests/cases/parser84/new-without-parenthesis.php
new file mode 100644
index 00000000..b2e381dc
--- /dev/null
+++ b/tests/cases/parser84/new-without-parenthesis.php
@@ -0,0 +1,3 @@
+<?php
+
+new Foobar()->bar();
diff --git a/tests/cases/parser84/new-without-parenthesis.php.diag b/tests/cases/parser84/new-without-parenthesis.php.diag
new file mode 100644
index 00000000..b2e381dc
--- /dev/null
+++ b/tests/cases/parser84/new-without-parenthesis.php.diag
@@ -0,0 +1,3 @@
+<?php
+
+new Foobar()->bar();
diff --git a/tests/cases/parser84/new-without-parenthesis.php.tree b/tests/cases/parser84/new-without-parenthesis.php.tree
new file mode 100644
index 00000000..b2e381dc
--- /dev/null
+++ b/tests/cases/parser84/new-without-parenthesis.php.tree
@@ -0,0 +1,3 @@
+<?php
+
+new Foobar()->bar();

From 8a42bdf799fd8868c85f6c6926014fefa5454ffc Mon Sep 17 00:00:00 2001
From: Daniel Leech <daniel@dantleech.com>
Date: Mon, 13 Jan 2025 20:19:07 +0000
Subject: [PATCH 07/11] Remove dead method

---
 src/Parser.php | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/src/Parser.php b/src/Parser.php
index 1e24b007..ce0bdbb4 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -3200,16 +3200,6 @@ private function parseSubscriptExpression($expression) : SubscriptExpression {
         return $subscriptExpression;
     }
 
-    private function parseNewObjectNoParenthesisMemberAccessExpression($expression):MemberAccessExpression {
-        $memberAccessExpression = new MemberAccessExpression();
-        $memberAccessExpression->parent = $expression;
-
-        $memberAccessExpression->dereferencableExpression = $expression;
-        $memberAccessExpression->arrowToken = $this->eat(TokenKind::ArrowToken, TokenKind::QuestionArrowToken);
-        $memberAccessExpression->memberName = $this->parseMemberName($memberAccessExpression);
-
-        return $memberAccessExpression;
-    }
     private function parseMemberAccessExpression($expression):MemberAccessExpression {
         $memberAccessExpression = new MemberAccessExpression();
         $memberAccessExpression->parent = $expression->parent;

From 0d3aad04d4c95f8be3f6b969eedadac2f9652838 Mon Sep 17 00:00:00 2001
From: Daniel Leech <daniel@dantleech.com>
Date: Sun, 26 Jan 2025 23:34:49 +0000
Subject: [PATCH 08/11] PHP 8.4 NULL deprecations fix

---
 src/Node.php   | 8 ++++----
 src/Parser.php | 2 +-
 src/Token.php  | 2 +-
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/Node.php b/src/Node.php
index 0f1e2e64..5de35751 100644
--- a/src/Node.php
+++ b/src/Node.php
@@ -151,7 +151,7 @@ public function getRoot() : Node {
      * @param callable|null $shouldDescendIntoChildrenFn
      * @return \Generator|Node[]|Token[]
      */
-    public function getDescendantNodesAndTokens(callable $shouldDescendIntoChildrenFn = null) {
+    public function getDescendantNodesAndTokens(?callable $shouldDescendIntoChildrenFn = null) {
         // TODO - write unit tests to prove invariants
         // (concatenating all descendant Tokens should produce document, concatenating all Nodes should produce document)
         foreach ($this->getChildNodesAndTokens() as $child) {
@@ -176,7 +176,7 @@ public function getDescendantNodesAndTokens(callable $shouldDescendIntoChildrenF
      * @param callable|null $shouldDescendIntoChildrenFn
      * @return void
      */
-    public function walkDescendantNodesAndTokens(callable $callback, callable $shouldDescendIntoChildrenFn = null) {
+    public function walkDescendantNodesAndTokens(callable $callback, ?callable $shouldDescendIntoChildrenFn = null) {
         // TODO - write unit tests to prove invariants
         // (concatenating all descendant Tokens should produce document, concatenating all Nodes should produce document)
         foreach (static::CHILD_NAMES as $name) {
@@ -209,7 +209,7 @@ public function walkDescendantNodesAndTokens(callable $callback, callable $shoul
      * @param callable|null $shouldDescendIntoChildrenFn
      * @return \Generator|Node[]
      */
-    public function getDescendantNodes(callable $shouldDescendIntoChildrenFn = null) {
+    public function getDescendantNodes(?callable $shouldDescendIntoChildrenFn = null) {
         foreach ($this->getChildNodes() as $child) {
             yield $child;
             if ($shouldDescendIntoChildrenFn === null || $shouldDescendIntoChildrenFn($child)) {
@@ -223,7 +223,7 @@ public function getDescendantNodes(callable $shouldDescendIntoChildrenFn = null)
      * @param callable|null $shouldDescendIntoChildrenFn
      * @return \Generator|Token[]
      */
-    public function getDescendantTokens(callable $shouldDescendIntoChildrenFn = null) {
+    public function getDescendantTokens(?callable $shouldDescendIntoChildrenFn = null) {
         foreach ($this->getChildNodesAndTokens() as $child) {
             if ($child instanceof Node) {
                 if ($shouldDescendIntoChildrenFn == null || $shouldDescendIntoChildrenFn($child)) {
diff --git a/src/Parser.php b/src/Parser.php
index ce0bdbb4..4821e587 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -175,7 +175,7 @@ protected function makeLexer(string $fileContents): TokenStreamProviderInterface
      * @param string $fileContents
      * @return SourceFileNode
      */
-    public function parseSourceFile(string $fileContents, string $uri = null) : SourceFileNode {
+    public function parseSourceFile(string $fileContents, ?string $uri = null) : SourceFileNode {
         $this->lexer = $this->makeLexer($fileContents);
 
         $this->reset();
diff --git a/src/Token.php b/src/Token.php
index 5fa1595d..dca04d29 100644
--- a/src/Token.php
+++ b/src/Token.php
@@ -42,7 +42,7 @@ public function getLeadingCommentsAndWhitespaceText(string $document) : string {
      * @param string|null $document
      * @return bool|null|string
      */
-    public function getText(string $document = null) {
+    public function getText(?string $document = null) {
         if ($document === null) {
             return null;
         }

From be9cbe541a5ccafa687fee9c4d7a8abe582a033d Mon Sep 17 00:00:00 2001
From: Tyson Andre <tysonandre775@hotmail.com>
Date: Sun, 11 Aug 2024 11:20:00 -0400
Subject: [PATCH 09/11] Support parsing of PHP 8.4 property hooks

As part of this, stricten the parsing of property names and use a
narrower syntax but reuse AssignmentExpression for backwards
compatibility of applications that originally used tolerant-php-parser's
result of parseExpression.

References:
https://wiki.php.net/rfc/property-hooks and the linked php-src PR's
zend_language_parser.y

https://wiki.php.net/rfc/property-hooks#abbreviated_syntax
https://wiki.php.net/rfc/property-hooks#interaction_with_constructor_property_promotion
---
 src/Node/Parameter.php                        |   5 +-
 src/Node/PropertyDeclaration.php              |   5 +
 src/Node/PropertyHook.php                     |  44 ++
 src/Node/PropertyHooks.php                    |  28 +
 src/Parser.php                                | 113 +++-
 tests/cases/parser/classMethods3.php.tree     |   6 +-
 tests/cases/parser/classMethods4.php.tree     |   6 +-
 .../cases/parser/dnfTypesParameter1.php.tree  |   3 +-
 .../cases/parser/dnfTypesParameter2.php.tree  |   3 +-
 .../cases/parser/dnfTypesParameter3.php.tree  |   3 +-
 .../cases/parser/dnfTypesParameter4.php.tree  |   3 +-
 .../cases/parser/dnfTypesParameter5.php.tree  |   3 +-
 tests/cases/parser/dnfTypesProperty1.php.tree |   1 +
 tests/cases/parser/functions10.php.tree       |   6 +-
 tests/cases/parser/functions11.php.tree       |   6 +-
 tests/cases/parser/functions12.php.tree       |   6 +-
 tests/cases/parser/functions13.php.tree       |   6 +-
 tests/cases/parser/functions14.php.tree       |   9 +-
 tests/cases/parser/functions15.php.tree       |   9 +-
 tests/cases/parser/functions16.php.tree       |   3 +-
 tests/cases/parser/functions17.php.tree       |   6 +-
 tests/cases/parser/functions18.php.tree       |   6 +-
 tests/cases/parser/functions3.php.tree        |   6 +-
 tests/cases/parser/functions6.php.tree        |   6 +-
 tests/cases/parser/functions7.php.tree        |   6 +-
 tests/cases/parser/functions8.php.tree        |   6 +-
 tests/cases/parser/functions9.php.tree        |   6 +-
 .../parser/interfaceDeclaration10.php.tree    |   6 +-
 .../parser/interfaceDeclaration8.php.tree     |   6 +-
 .../parser/interfaceDeclaration9.php.tree     |   6 +-
 .../intersectionTypeParameter1.php.tree       |   3 +-
 .../intersectionTypeParameter2.php.tree       |   3 +-
 tests/cases/parser/paramHook1.php             |   8 +
 tests/cases/parser/paramHook1.php.diag        |   1 +
 tests/cases/parser/paramHook1.php.tree        | 211 +++++++
 .../cases/parser/parameterPromotion1.php.tree |   9 +-
 .../cases/parser/parameterPromotion2.php.tree |   3 +-
 tests/cases/parser/parameterTypes1.php.tree   |   6 +-
 .../parser/propertyDeclaration1.php.tree      |   1 +
 tests/cases/parser/propertyDeclaration10.php  |   4 +
 .../parser/propertyDeclaration10.php.diag     |   1 +
 .../parser/propertyDeclaration10.php.tree     | 105 ++++
 tests/cases/parser/propertyDeclaration11.php  |   4 +
 .../parser/propertyDeclaration11.php.diag     |  20 +
 .../parser/propertyDeclaration11.php.tree     | 126 ++++
 tests/cases/parser/propertyDeclaration12.php  |   4 +
 .../parser/propertyDeclaration12.php.diag     |  14 +
 .../parser/propertyDeclaration12.php.tree     | 127 ++++
 .../parser/propertyDeclaration2.php.tree      |   1 +
 .../parser/propertyDeclaration3.php.tree      |   1 +
 .../parser/propertyDeclaration4.php.tree      |   1 +
 .../parser/propertyDeclaration5.php.tree      |   1 +
 tests/cases/parser/propertyDeclaration8.php   |   4 +
 .../parser/propertyDeclaration8.php.diag      |  26 +
 .../parser/propertyDeclaration8.php.tree      | 112 ++++
 tests/cases/parser/propertyDeclaration9.php   |   4 +
 .../parser/propertyDeclaration9.php.diag      |   8 +
 .../parser/propertyDeclaration9.php.tree      | 114 ++++
 tests/cases/parser/propertyHook1.php          |   4 +
 tests/cases/parser/propertyHook1.php.diag     |   1 +
 tests/cases/parser/propertyHook1.php.tree     |  93 +++
 tests/cases/parser/propertyHook2.php          |  12 +
 tests/cases/parser/propertyHook2.php.diag     |   1 +
 tests/cases/parser/propertyHook2.php.tree     | 303 ++++++++++
 tests/cases/parser/propertyHook3.php          |  15 +
 tests/cases/parser/propertyHook3.php.diag     |   1 +
 tests/cases/parser/propertyHook3.php.tree     | 569 ++++++++++++++++++
 tests/cases/parser/true.php.tree              |   3 +-
 tests/cases/parser/typedProperties.php.tree   |   4 +
 .../parser/typedPropertiesNoType.php.tree     |   1 +
 .../parser/typedPropertiesSimple.php.tree     |   1 +
 .../cases/parser/unionTypeParameter1.php.tree |   3 +-
 .../cases/parser/unionTypeParameter2.php.tree |   3 +-
 tests/cases/parser/unionTypeProperty.php.tree |   2 +
 .../cases/parser/unionTypeProperty2.php.tree  |   1 +
 tests/cases/parser/yieldExpression18.php.tree |   3 +-
 tests/cases/parser/yieldExpression19.php.tree |   3 +-
 tests/cases/parser74/arrowFunction.php.tree   |  18 +-
 .../parser74/arrowFunctionFallback.php.tree   |  15 +-
 .../parser74/arrowFunctionTypes.php.tree      |  15 +-
 .../parser74/arrowFunctionUnionTypes.php.tree |  12 +-
 tests/cases/parser80/attributes18.php.tree    |   3 +-
 tests/cases/parser80/attributes20.php         |   7 +
 tests/cases/parser80/attributes20.php.diag    |   1 +
 tests/cases/parser80/attributes20.php.tree    | 172 ++++++
 tests/cases/parser80/attributes4.php.tree     |   1 +
 tests/cases/parser80/attributes5.php.tree     |   3 +-
 tests/cases/parser80/mixedType1.php.tree      |   3 +-
 .../cases/parser81/intersection_type.php.tree |   7 +-
 .../cases/parser81/readonly_property.php.tree |   2 +
 90 files changed, 2447 insertions(+), 85 deletions(-)
 create mode 100644 src/Node/PropertyHook.php
 create mode 100644 src/Node/PropertyHooks.php
 create mode 100644 tests/cases/parser/paramHook1.php
 create mode 100644 tests/cases/parser/paramHook1.php.diag
 create mode 100644 tests/cases/parser/paramHook1.php.tree
 create mode 100644 tests/cases/parser/propertyDeclaration10.php
 create mode 100644 tests/cases/parser/propertyDeclaration10.php.diag
 create mode 100644 tests/cases/parser/propertyDeclaration10.php.tree
 create mode 100644 tests/cases/parser/propertyDeclaration11.php
 create mode 100644 tests/cases/parser/propertyDeclaration11.php.diag
 create mode 100644 tests/cases/parser/propertyDeclaration11.php.tree
 create mode 100644 tests/cases/parser/propertyDeclaration12.php
 create mode 100644 tests/cases/parser/propertyDeclaration12.php.diag
 create mode 100644 tests/cases/parser/propertyDeclaration12.php.tree
 create mode 100644 tests/cases/parser/propertyDeclaration8.php
 create mode 100644 tests/cases/parser/propertyDeclaration8.php.diag
 create mode 100644 tests/cases/parser/propertyDeclaration8.php.tree
 create mode 100644 tests/cases/parser/propertyDeclaration9.php
 create mode 100644 tests/cases/parser/propertyDeclaration9.php.diag
 create mode 100644 tests/cases/parser/propertyDeclaration9.php.tree
 create mode 100644 tests/cases/parser/propertyHook1.php
 create mode 100644 tests/cases/parser/propertyHook1.php.diag
 create mode 100644 tests/cases/parser/propertyHook1.php.tree
 create mode 100644 tests/cases/parser/propertyHook2.php
 create mode 100644 tests/cases/parser/propertyHook2.php.diag
 create mode 100644 tests/cases/parser/propertyHook2.php.tree
 create mode 100644 tests/cases/parser/propertyHook3.php
 create mode 100644 tests/cases/parser/propertyHook3.php.diag
 create mode 100644 tests/cases/parser/propertyHook3.php.tree
 create mode 100644 tests/cases/parser80/attributes20.php
 create mode 100644 tests/cases/parser80/attributes20.php.diag
 create mode 100644 tests/cases/parser80/attributes20.php.tree

diff --git a/src/Node/Parameter.php b/src/Node/Parameter.php
index d3fde6f5..61f940b0 100644
--- a/src/Node/Parameter.php
+++ b/src/Node/Parameter.php
@@ -31,6 +31,8 @@ class Parameter extends Node {
     public $equalsToken;
     /** @var null|Expression */
     public $default;
+    /** @var PropertyHooks|null */
+    public $propertyHooks;
 
     const CHILD_NAMES = [
         'attributes',
@@ -42,7 +44,8 @@ class Parameter extends Node {
         'dotDotDotToken',
         'variableName',
         'equalsToken',
-        'default'
+        'default',
+        'propertyHooks',
     ];
 
     public function isVariadic() {
diff --git a/src/Node/PropertyDeclaration.php b/src/Node/PropertyDeclaration.php
index 1995aed5..a30cefae 100644
--- a/src/Node/PropertyDeclaration.php
+++ b/src/Node/PropertyDeclaration.php
@@ -10,6 +10,7 @@
 use Microsoft\PhpParser\ModifiedTypeInterface;
 use Microsoft\PhpParser\ModifiedTypeTrait;
 use Microsoft\PhpParser\Node;
+use Microsoft\PhpParser\Node\PropertyHooks;
 use Microsoft\PhpParser\Node\DelimitedList\QualifiedNameList;
 use Microsoft\PhpParser\Token;
 
@@ -28,6 +29,9 @@ class PropertyDeclaration extends Node implements ModifiedTypeInterface {
     /** @var DelimitedList\ExpressionList */
     public $propertyElements;
 
+    /** @var PropertyHooks|null */
+    public $propertyHooks;
+
     /** @var Token */
     public $semicolon;
 
@@ -37,6 +41,7 @@ class PropertyDeclaration extends Node implements ModifiedTypeInterface {
         'questionToken',
         'typeDeclarationList',
         'propertyElements',
+        'propertyHooks',
         'semicolon'
     ];
 }
diff --git a/src/Node/PropertyHook.php b/src/Node/PropertyHook.php
new file mode 100644
index 00000000..9d2cadf0
--- /dev/null
+++ b/src/Node/PropertyHook.php
@@ -0,0 +1,44 @@
+<?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\Node\Statement\CompoundStatementNode;
+use Microsoft\PhpParser\Token;
+
+class PropertyHook extends Node {
+    /** @var AttributeGroup[]|null */
+    public $attributes;
+    /** @var Token */
+    public $byRefToken;
+    /** @var Token */
+    public $name;
+    /** @var Token|null */
+    public $openParen;
+    /** @var DelimitedList\ParameterDeclarationList|null */
+    public $parameters;
+    /** @var Token|null */
+    public $closeParen;
+    /** @var Token|null */
+    public $arrowToken;
+    /** @var CompoundStatementNode|Expression|Token|null */
+    public $body;
+    /** @var Token|null */
+    public $semicolon;
+
+    const CHILD_NAMES = [
+        'attributes',
+        'byRefToken',
+        'name',
+        'openParen',
+        'parameters',
+        'closeParen',
+        'arrowToken',
+        'body',
+        'semicolon',
+    ];
+}
diff --git a/src/Node/PropertyHooks.php b/src/Node/PropertyHooks.php
new file mode 100644
index 00000000..6570241c
--- /dev/null
+++ b/src/Node/PropertyHooks.php
@@ -0,0 +1,28 @@
+<?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\Node\PropertyHooks;
+use Microsoft\PhpParser\Token;
+
+class PropertyHooks extends Node {
+    /** @var Token */
+    public $openBrace;
+
+    /** @var PropertyHook[] */
+    public $hookDeclarations;
+
+    /** @var Token */
+    public $closeBrace;
+
+    const CHILD_NAMES = [
+        'openBrace',
+        'hookDeclarations',
+        'closeBrace'
+    ];
+}
diff --git a/src/Parser.php b/src/Parser.php
index 4821e587..dba052e1 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -74,6 +74,8 @@
 use Microsoft\PhpParser\Node\NumericLiteral;
 use Microsoft\PhpParser\Node\ParenthesizedIntersectionType;
 use Microsoft\PhpParser\Node\PropertyDeclaration;
+use Microsoft\PhpParser\Node\PropertyHooks;
+use Microsoft\PhpParser\Node\PropertyHook;
 use Microsoft\PhpParser\Node\ReservedWord;
 use Microsoft\PhpParser\Node\StringLiteral;
 use Microsoft\PhpParser\Node\MethodDeclaration;
@@ -877,6 +879,10 @@ private function parseParameterFn() {
                 // TODO add post-parse rule that checks for invalid assignments
                 $parameter->default = $this->parseExpression($parameter);
             }
+
+            if ($this->token->kind === TokenKind::OpenBraceToken) {
+                $parameter->propertyHooks = $this->parsePropertyHooks($parameter);
+            }
             return $parameter;
         };
     }
@@ -3437,12 +3443,115 @@ private function parsePropertyDeclaration($parentNode, $modifiers, $questionToke
         } elseif ($questionToken) {
             $propertyDeclaration->typeDeclarationList = new MissingToken(TokenKind::PropertyType, $this->token->fullStart);
         }
-        $propertyDeclaration->propertyElements = $this->parseExpressionList($propertyDeclaration);
-        $propertyDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken);
+        $propertyDeclaration->propertyElements = $this->parsePropertyNameList($propertyDeclaration);
+        if ($this->token->kind === TokenKind::OpenBraceToken) {
+            $propertyDeclaration->propertyHooks = $this->parsePropertyHooks($propertyDeclaration);
+        } else {
+            $propertyDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken);
+        }
 
         return $propertyDeclaration;
     }
 
+    /**
+     * @param PropertyDeclaration $parentNode
+     * @return DelimitedList\VariableNameList
+     */
+    private function parsePropertyNameList($parentNode) {
+        // XXX this used to be implemented with parseExpressionList so keep the same classes.
+        return $this->parseDelimitedList(
+            DelimitedList\ExpressionList::class,
+            TokenKind::CommaToken,
+            function ($token) {
+                return $token->kind === TokenKind::VariableName;
+            },
+            $this->parsePropertyVariableNameAndDefault(),
+            $parentNode
+        );
+    }
+
+    private function parsePropertyVariableNameAndDefault() {
+        return function ($parentNode) {
+            // Imitate the format that parseExpression would have returned from when
+            // parseExpression was originally used.
+            // This is more precise and rules out parse errors such as `public $propName + 2;`
+            // This approach also avoids conflict with the deprecated $x{expr} array access syntax.
+            $variable = new Variable();
+            $variable->name = $this->eat1(TokenKind::VariableName);
+            $equalsToken = $this->eatOptional1(TokenKind::EqualsToken);
+            if ($equalsToken === null) {
+                $variable->parent = $parentNode;
+                return $variable;
+            }
+
+            $byRefToken = $this->eatOptional1(TokenKind::AmpersandToken); // byRef default is nonsense, but this is a compile-time error instead of a parse error.
+            $rightOperand = $this->parseExpression($parentNode); // gets overridden in makeBinaryExpression
+            return $this->makeBinaryExpression($variable, $equalsToken, $byRefToken, $rightOperand, $parentNode);
+        };
+    }
+
+    /**
+     * @param PropertyDeclaration|Parameter $parent
+     * @return PropertyHooks
+     */
+    private function parsePropertyHooks(Node $parent) {
+        $propertyHooks = new PropertyHooks();
+        $propertyHooks->parent = $parent;
+        $propertyHooks->openBrace = $this->eat1(TokenKind::OpenBraceToken);
+        $hooks = [];
+        while (in_array($this->getCurrentToken()->kind, self::PROPERTY_HOOK_START_TOKENS, true)) {
+            $hooks[] = $this->parsePropertyHook($propertyHooks);
+        }
+        $propertyHooks->hookDeclarations = $hooks;
+        $propertyHooks->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
+        return $propertyHooks;
+    }
+
+    const PROPERTY_HOOK_START_TOKENS = [
+        TokenKind::Name,
+        TokenKind::AmpersandToken, // by reference
+        TokenKind::AttributeToken,
+    ];
+
+    private function isPropertyHookStart() {
+        return function ($token) {
+            return \in_array($token->kind, self::PROPERTY_HOOK_START_TOKENS, true);
+        };
+    }
+
+    /**
+     * @param PropertyHooks $parentNode
+     * @return PropertyHook
+     */
+    private function parsePropertyHook($parentNode) {
+        $node = new PropertyHook();
+        $node->parent = $parentNode;
+        if ($this->getCurrentToken()->kind === TokenKind::AttributeToken) {
+            $node->attributes = $this->parseAttributeGroups($node);
+        }
+        $node->byRefToken = $this->eatOptional1(TokenKind::AmpersandToken);
+        $node->name = $this->eat1(TokenKind::Name); // "get" or "set" - other values are compile errors, not parse errors.
+        $node->openParen = $this->eatOptional1(TokenKind::OpenParenToken);
+        if ($node->openParen) {
+            $node->parameters = $this->parseDelimitedList(
+                DelimitedList\ParameterDeclarationList::class,
+                TokenKind::CommaToken,
+                $this->isParameterStartFn(),
+                $this->parseParameterFn(),
+                $node);
+            $node->closeParen = $this->eat1(TokenKind::CloseParenToken);
+        }
+        // e.g. `get => expr;` or `get { return $expr; }`
+        $node->arrowToken = $this->eatOptional1(TokenKind::DoubleArrowToken);
+        if ($node->arrowToken) {
+            $node->body = $this->parseExpression($node);
+            $node->semicolon = $this->eat1(TokenKind::SemicolonToken);
+        } else {
+            $node->body = $this->parseCompoundStatement($node);
+        }
+        return $node;
+    }
+
     /**
      * Parse a comma separated qualified name list (e.g. interfaces implemented by a class)
      *
diff --git a/tests/cases/parser/classMethods3.php.tree b/tests/cases/parser/classMethods3.php.tree
index ff156ef9..96e3d20a 100644
--- a/tests/cases/parser/classMethods3.php.tree
+++ b/tests/cases/parser/classMethods3.php.tree
@@ -72,7 +72,8 @@
                                                                 "textLength": 8
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     },
                                                     {
@@ -93,7 +94,8 @@
                                                                 "textLength": 6
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     }
                                                 ]
diff --git a/tests/cases/parser/classMethods4.php.tree b/tests/cases/parser/classMethods4.php.tree
index eb3536dd..3e147ad6 100644
--- a/tests/cases/parser/classMethods4.php.tree
+++ b/tests/cases/parser/classMethods4.php.tree
@@ -72,7 +72,8 @@
                                                                 "textLength": 8
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     },
                                                     {
@@ -93,7 +94,8 @@
                                                                 "textLength": 6
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     }
                                                 ]
diff --git a/tests/cases/parser/dnfTypesParameter1.php.tree b/tests/cases/parser/dnfTypesParameter1.php.tree
index 6da52940..a9ad6e37 100644
--- a/tests/cases/parser/dnfTypesParameter1.php.tree
+++ b/tests/cases/parser/dnfTypesParameter1.php.tree
@@ -111,7 +111,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/dnfTypesParameter2.php.tree b/tests/cases/parser/dnfTypesParameter2.php.tree
index 22b9aedd..218278a0 100644
--- a/tests/cases/parser/dnfTypesParameter2.php.tree
+++ b/tests/cases/parser/dnfTypesParameter2.php.tree
@@ -100,7 +100,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/dnfTypesParameter3.php.tree b/tests/cases/parser/dnfTypesParameter3.php.tree
index 9cdf7078..00311b61 100644
--- a/tests/cases/parser/dnfTypesParameter3.php.tree
+++ b/tests/cases/parser/dnfTypesParameter3.php.tree
@@ -71,7 +71,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/dnfTypesParameter4.php.tree b/tests/cases/parser/dnfTypesParameter4.php.tree
index 91a5fd2a..7054ba96 100644
--- a/tests/cases/parser/dnfTypesParameter4.php.tree
+++ b/tests/cases/parser/dnfTypesParameter4.php.tree
@@ -73,7 +73,8 @@
                                             "textLength": 0
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/dnfTypesParameter5.php.tree b/tests/cases/parser/dnfTypesParameter5.php.tree
index 719e80f9..ff37d00e 100644
--- a/tests/cases/parser/dnfTypesParameter5.php.tree
+++ b/tests/cases/parser/dnfTypesParameter5.php.tree
@@ -104,7 +104,8 @@
                                             "textLength": 0
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/dnfTypesProperty1.php.tree b/tests/cases/parser/dnfTypesProperty1.php.tree
index 8356cefc..82aaca40 100644
--- a/tests/cases/parser/dnfTypesProperty1.php.tree
+++ b/tests/cases/parser/dnfTypesProperty1.php.tree
@@ -135,6 +135,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/functions10.php.tree b/tests/cases/parser/functions10.php.tree
index 4d0b84a1..e66f895a 100644
--- a/tests/cases/parser/functions10.php.tree
+++ b/tests/cases/parser/functions10.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -71,7 +72,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions11.php.tree b/tests/cases/parser/functions11.php.tree
index d05667b1..8fb4a4d9 100644
--- a/tests/cases/parser/functions11.php.tree
+++ b/tests/cases/parser/functions11.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -88,7 +89,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions12.php.tree b/tests/cases/parser/functions12.php.tree
index be4536e8..8f5617f7 100644
--- a/tests/cases/parser/functions12.php.tree
+++ b/tests/cases/parser/functions12.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -85,7 +86,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions13.php.tree b/tests/cases/parser/functions13.php.tree
index f034806c..103c01fe 100644
--- a/tests/cases/parser/functions13.php.tree
+++ b/tests/cases/parser/functions13.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -85,7 +86,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
diff --git a/tests/cases/parser/functions14.php.tree b/tests/cases/parser/functions14.php.tree
index 4f274fdc..3b337ebc 100644
--- a/tests/cases/parser/functions14.php.tree
+++ b/tests/cases/parser/functions14.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -85,7 +86,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -106,7 +108,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions15.php.tree b/tests/cases/parser/functions15.php.tree
index 13421349..0186b9af 100644
--- a/tests/cases/parser/functions15.php.tree
+++ b/tests/cases/parser/functions15.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -85,7 +86,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -109,7 +111,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions16.php.tree b/tests/cases/parser/functions16.php.tree
index 15d6d764..62944fe6 100644
--- a/tests/cases/parser/functions16.php.tree
+++ b/tests/cases/parser/functions16.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions17.php.tree b/tests/cases/parser/functions17.php.tree
index 2d8cd231..a7d61fcb 100644
--- a/tests/cases/parser/functions17.php.tree
+++ b/tests/cases/parser/functions17.php.tree
@@ -64,7 +64,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -97,7 +98,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions18.php.tree b/tests/cases/parser/functions18.php.tree
index 56fc06f5..5b3a6b91 100644
--- a/tests/cases/parser/functions18.php.tree
+++ b/tests/cases/parser/functions18.php.tree
@@ -61,7 +61,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -91,7 +92,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions3.php.tree b/tests/cases/parser/functions3.php.tree
index 4b2182ed..f1c6a9b0 100644
--- a/tests/cases/parser/functions3.php.tree
+++ b/tests/cases/parser/functions3.php.tree
@@ -61,7 +61,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -91,7 +92,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions6.php.tree b/tests/cases/parser/functions6.php.tree
index 9e09f45c..6f80a8b1 100644
--- a/tests/cases/parser/functions6.php.tree
+++ b/tests/cases/parser/functions6.php.tree
@@ -55,7 +55,8 @@
                                                     "textLength": 2
                                                 }
                                             }
-                                        }
+                                        },
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -76,7 +77,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions7.php.tree b/tests/cases/parser/functions7.php.tree
index bb10e305..555f7f1c 100644
--- a/tests/cases/parser/functions7.php.tree
+++ b/tests/cases/parser/functions7.php.tree
@@ -51,7 +51,8 @@
                                             "error": "MissingToken",
                                             "kind": "Expression",
                                             "textLength": 0
-                                        }
+                                        },
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -72,7 +73,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions8.php.tree b/tests/cases/parser/functions8.php.tree
index f3c4953f..07dcd1ba 100644
--- a/tests/cases/parser/functions8.php.tree
+++ b/tests/cases/parser/functions8.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -65,7 +66,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/functions9.php.tree b/tests/cases/parser/functions9.php.tree
index cf02019b..ae02a5f3 100644
--- a/tests/cases/parser/functions9.php.tree
+++ b/tests/cases/parser/functions9.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -68,7 +69,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/interfaceDeclaration10.php.tree b/tests/cases/parser/interfaceDeclaration10.php.tree
index a96d2a5e..7f20c230 100644
--- a/tests/cases/parser/interfaceDeclaration10.php.tree
+++ b/tests/cases/parser/interfaceDeclaration10.php.tree
@@ -69,7 +69,8 @@
                                                                 "textLength": 2
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     },
                                                     {
@@ -100,7 +101,8 @@
                                                                         "textLength": 1
                                                                     }
                                                                 }
-                                                            }
+                                                            },
+                                                            "propertyHooks": null
                                                         }
                                                     }
                                                 ]
diff --git a/tests/cases/parser/interfaceDeclaration8.php.tree b/tests/cases/parser/interfaceDeclaration8.php.tree
index ad10fd8b..c9afe318 100644
--- a/tests/cases/parser/interfaceDeclaration8.php.tree
+++ b/tests/cases/parser/interfaceDeclaration8.php.tree
@@ -69,7 +69,8 @@
                                                                 "textLength": 2
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     },
                                                     {
@@ -90,7 +91,8 @@
                                                                 "textLength": 2
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     }
                                                 ]
diff --git a/tests/cases/parser/interfaceDeclaration9.php.tree b/tests/cases/parser/interfaceDeclaration9.php.tree
index bbcf73df..760dac7e 100644
--- a/tests/cases/parser/interfaceDeclaration9.php.tree
+++ b/tests/cases/parser/interfaceDeclaration9.php.tree
@@ -69,7 +69,8 @@
                                                                 "textLength": 2
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     },
                                                     {
@@ -100,7 +101,8 @@
                                                                         "textLength": 1
                                                                     }
                                                                 }
-                                                            }
+                                                            },
+                                                            "propertyHooks": null
                                                         }
                                                     }
                                                 ]
diff --git a/tests/cases/parser/intersectionTypeParameter1.php.tree b/tests/cases/parser/intersectionTypeParameter1.php.tree
index 6104ca92..103b148f 100644
--- a/tests/cases/parser/intersectionTypeParameter1.php.tree
+++ b/tests/cases/parser/intersectionTypeParameter1.php.tree
@@ -78,7 +78,8 @@
                                             "textLength": 0
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/intersectionTypeParameter2.php.tree b/tests/cases/parser/intersectionTypeParameter2.php.tree
index 44cf3b08..11ffc9bb 100644
--- a/tests/cases/parser/intersectionTypeParameter2.php.tree
+++ b/tests/cases/parser/intersectionTypeParameter2.php.tree
@@ -97,7 +97,8 @@
                                             "textLength": 0
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/paramHook1.php b/tests/cases/parser/paramHook1.php
new file mode 100644
index 00000000..db250448
--- /dev/null
+++ b/tests/cases/parser/paramHook1.php
@@ -0,0 +1,8 @@
+<?php
+
+class User
+{
+    public function __construct(
+        public string $username { set => strtolower($value); }
+    ) {}
+}
diff --git a/tests/cases/parser/paramHook1.php.diag b/tests/cases/parser/paramHook1.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser/paramHook1.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser/paramHook1.php.tree b/tests/cases/parser/paramHook1.php.tree
new file mode 100644
index 00000000..d4f60b53
--- /dev/null
+++ b/tests/cases/parser/paramHook1.php.tree
@@ -0,0 +1,211 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 4
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "MethodDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "functionKeyword": {
+                                            "kind": "FunctionKeyword",
+                                            "textLength": 8
+                                        },
+                                        "byRefToken": null,
+                                        "name": {
+                                            "kind": "Name",
+                                            "textLength": 11
+                                        },
+                                        "openParen": {
+                                            "kind": "OpenParenToken",
+                                            "textLength": 1
+                                        },
+                                        "parameters": {
+                                            "ParameterDeclarationList": {
+                                                "children": [
+                                                    {
+                                                        "Parameter": {
+                                                            "attributes": null,
+                                                            "visibilityToken": {
+                                                                "kind": "PublicKeyword",
+                                                                "textLength": 6
+                                                            },
+                                                            "modifiers": null,
+                                                            "questionToken": null,
+                                                            "typeDeclarationList": {
+                                                                "QualifiedNameList": {
+                                                                    "children": [
+                                                                        {
+                                                                            "kind": "StringReservedWord",
+                                                                            "textLength": 6
+                                                                        }
+                                                                    ]
+                                                                }
+                                                            },
+                                                            "byRefToken": null,
+                                                            "dotDotDotToken": null,
+                                                            "variableName": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 9
+                                                            },
+                                                            "equalsToken": null,
+                                                            "default": null,
+                                                            "propertyHooks": {
+                                                                "PropertyHooks": {
+                                                                    "openBrace": {
+                                                                        "kind": "OpenBraceToken",
+                                                                        "textLength": 1
+                                                                    },
+                                                                    "hookDeclarations": [
+                                                                        {
+                                                                            "PropertyHook": {
+                                                                                "attributes": null,
+                                                                                "byRefToken": null,
+                                                                                "name": {
+                                                                                    "kind": "Name",
+                                                                                    "textLength": 3
+                                                                                },
+                                                                                "openParen": null,
+                                                                                "parameters": null,
+                                                                                "closeParen": null,
+                                                                                "arrowToken": {
+                                                                                    "kind": "DoubleArrowToken",
+                                                                                    "textLength": 2
+                                                                                },
+                                                                                "body": {
+                                                                                    "CallExpression": {
+                                                                                        "callableExpression": {
+                                                                                            "QualifiedName": {
+                                                                                                "globalSpecifier": null,
+                                                                                                "relativeSpecifier": null,
+                                                                                                "nameParts": [
+                                                                                                    {
+                                                                                                        "kind": "Name",
+                                                                                                        "textLength": 10
+                                                                                                    }
+                                                                                                ]
+                                                                                            }
+                                                                                        },
+                                                                                        "openParen": {
+                                                                                            "kind": "OpenParenToken",
+                                                                                            "textLength": 1
+                                                                                        },
+                                                                                        "argumentExpressionList": {
+                                                                                            "ArgumentExpressionList": {
+                                                                                                "children": [
+                                                                                                    {
+                                                                                                        "ArgumentExpression": {
+                                                                                                            "name": null,
+                                                                                                            "colonToken": null,
+                                                                                                            "dotDotDotToken": null,
+                                                                                                            "expression": {
+                                                                                                                "Variable": {
+                                                                                                                    "dollar": null,
+                                                                                                                    "name": {
+                                                                                                                        "kind": "VariableName",
+                                                                                                                        "textLength": 6
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            }
+                                                                                                        }
+                                                                                                    }
+                                                                                                ]
+                                                                                            }
+                                                                                        },
+                                                                                        "closeParen": {
+                                                                                            "kind": "CloseParenToken",
+                                                                                            "textLength": 1
+                                                                                        }
+                                                                                    }
+                                                                                },
+                                                                                "semicolon": {
+                                                                                    "kind": "SemicolonToken",
+                                                                                    "textLength": 1
+                                                                                }
+                                                                            }
+                                                                        }
+                                                                    ],
+                                                                    "closeBrace": {
+                                                                        "kind": "CloseBraceToken",
+                                                                        "textLength": 1
+                                                                    }
+                                                                }
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "closeParen": {
+                                            "kind": "CloseParenToken",
+                                            "textLength": 1
+                                        },
+                                        "colonToken": null,
+                                        "questionToken": null,
+                                        "returnTypeList": null,
+                                        "compoundStatementOrSemicolon": {
+                                            "CompoundStatementNode": {
+                                                "openBrace": {
+                                                    "kind": "OpenBraceToken",
+                                                    "textLength": 1
+                                                },
+                                                "statements": [],
+                                                "closeBrace": {
+                                                    "kind": "CloseBraceToken",
+                                                    "textLength": 1
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/parameterPromotion1.php.tree b/tests/cases/parser/parameterPromotion1.php.tree
index 44e83ae6..a3388b17 100644
--- a/tests/cases/parser/parameterPromotion1.php.tree
+++ b/tests/cases/parser/parameterPromotion1.php.tree
@@ -75,7 +75,8 @@
                                                                 "textLength": 2
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     },
                                                     {
@@ -108,7 +109,8 @@
                                                                 "textLength": 2
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     },
                                                     {
@@ -132,7 +134,8 @@
                                                                 "textLength": 2
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     }
                                                 ]
diff --git a/tests/cases/parser/parameterPromotion2.php.tree b/tests/cases/parser/parameterPromotion2.php.tree
index 62983624..e30ce698 100644
--- a/tests/cases/parser/parameterPromotion2.php.tree
+++ b/tests/cases/parser/parameterPromotion2.php.tree
@@ -80,7 +80,8 @@
                                                                 "textLength": 4
                                                             },
                                                             "equalsToken": null,
-                                                            "default": null
+                                                            "default": null,
+                                                            "propertyHooks": null
                                                         }
                                                     }
                                                 ]
diff --git a/tests/cases/parser/parameterTypes1.php.tree b/tests/cases/parser/parameterTypes1.php.tree
index 55f2f7c9..c97f5d07 100644
--- a/tests/cases/parser/parameterTypes1.php.tree
+++ b/tests/cases/parser/parameterTypes1.php.tree
@@ -53,7 +53,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -83,7 +84,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/propertyDeclaration1.php.tree b/tests/cases/parser/propertyDeclaration1.php.tree
index 9a97493e..4b624572 100644
--- a/tests/cases/parser/propertyDeclaration1.php.tree
+++ b/tests/cases/parser/propertyDeclaration1.php.tree
@@ -59,6 +59,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/propertyDeclaration10.php b/tests/cases/parser/propertyDeclaration10.php
new file mode 100644
index 00000000..5d74ab3a
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration10.php
@@ -0,0 +1,4 @@
+<?php
+class A {
+    public $x = & $y; // compile time error, not a syntax error
+}
diff --git a/tests/cases/parser/propertyDeclaration10.php.diag b/tests/cases/parser/propertyDeclaration10.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration10.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser/propertyDeclaration10.php.tree b/tests/cases/parser/propertyDeclaration10.php.tree
new file mode 100644
index 00000000..94c2d62d
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration10.php.tree
@@ -0,0 +1,105 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 1
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": null,
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "AssignmentExpression": {
+                                                            "leftOperand": {
+                                                                "Variable": {
+                                                                    "dollar": null,
+                                                                    "name": {
+                                                                        "kind": "VariableName",
+                                                                        "textLength": 2
+                                                                    }
+                                                                }
+                                                            },
+                                                            "operator": {
+                                                                "kind": "EqualsToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "byRef": {
+                                                                "kind": "AmpersandToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "rightOperand": {
+                                                                "Variable": {
+                                                                    "dollar": null,
+                                                                    "name": {
+                                                                        "kind": "VariableName",
+                                                                        "textLength": 2
+                                                                    }
+                                                                }
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": null,
+                                        "semicolon": {
+                                            "kind": "SemicolonToken",
+                                            "textLength": 1
+                                        }
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/propertyDeclaration11.php b/tests/cases/parser/propertyDeclaration11.php
new file mode 100644
index 00000000..fb089c56
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration11.php
@@ -0,0 +1,4 @@
+<?php
+class A {
+    static $x = , $y =
+}
diff --git a/tests/cases/parser/propertyDeclaration11.php.diag b/tests/cases/parser/propertyDeclaration11.php.diag
new file mode 100644
index 00000000..2adc63dc
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration11.php.diag
@@ -0,0 +1,20 @@
+[
+    {
+        "kind": 0,
+        "message": "'Expression' expected.",
+        "start": 31,
+        "length": 0
+    },
+    {
+        "kind": 0,
+        "message": "'Expression' expected.",
+        "start": 38,
+        "length": 0
+    },
+    {
+        "kind": 0,
+        "message": "';' expected.",
+        "start": 38,
+        "length": 0
+    }
+]
\ No newline at end of file
diff --git a/tests/cases/parser/propertyDeclaration11.php.tree b/tests/cases/parser/propertyDeclaration11.php.tree
new file mode 100644
index 00000000..9256da56
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration11.php.tree
@@ -0,0 +1,126 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 1
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "StaticKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": null,
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "AssignmentExpression": {
+                                                            "leftOperand": {
+                                                                "Variable": {
+                                                                    "dollar": null,
+                                                                    "name": {
+                                                                        "kind": "VariableName",
+                                                                        "textLength": 2
+                                                                    }
+                                                                }
+                                                            },
+                                                            "operator": {
+                                                                "kind": "EqualsToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "byRef": null,
+                                                            "rightOperand": {
+                                                                "error": "MissingToken",
+                                                                "kind": "Expression",
+                                                                "textLength": 0
+                                                            }
+                                                        }
+                                                    },
+                                                    {
+                                                        "kind": "CommaToken",
+                                                        "textLength": 1
+                                                    },
+                                                    {
+                                                        "AssignmentExpression": {
+                                                            "leftOperand": {
+                                                                "Variable": {
+                                                                    "dollar": null,
+                                                                    "name": {
+                                                                        "kind": "VariableName",
+                                                                        "textLength": 2
+                                                                    }
+                                                                }
+                                                            },
+                                                            "operator": {
+                                                                "kind": "EqualsToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "byRef": null,
+                                                            "rightOperand": {
+                                                                "error": "MissingToken",
+                                                                "kind": "Expression",
+                                                                "textLength": 0
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": null,
+                                        "semicolon": {
+                                            "error": "MissingToken",
+                                            "kind": "SemicolonToken",
+                                            "textLength": 0
+                                        }
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/propertyDeclaration12.php b/tests/cases/parser/propertyDeclaration12.php
new file mode 100644
index 00000000..e4683773
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration12.php
@@ -0,0 +1,4 @@
+<?php
+class A {
+    public ${invalid} = 123; // This is a parse error.
+}
diff --git a/tests/cases/parser/propertyDeclaration12.php.diag b/tests/cases/parser/propertyDeclaration12.php.diag
new file mode 100644
index 00000000..0b288a5f
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration12.php.diag
@@ -0,0 +1,14 @@
+[
+    {
+        "kind": 0,
+        "message": "'}' expected.",
+        "start": 26,
+        "length": 0
+    },
+    {
+        "kind": 0,
+        "message": "Unexpected '}'",
+        "start": 71,
+        "length": 1
+    }
+]
\ No newline at end of file
diff --git a/tests/cases/parser/propertyDeclaration12.php.tree b/tests/cases/parser/propertyDeclaration12.php.tree
new file mode 100644
index 00000000..5702f31d
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration12.php.tree
@@ -0,0 +1,127 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 1
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "MissingMemberDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": null
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "error": "MissingToken",
+                                "kind": "CloseBraceToken",
+                                "textLength": 0
+                            }
+                        }
+                    }
+                }
+            },
+            {
+                "ExpressionStatement": {
+                    "expression": {
+                        "AssignmentExpression": {
+                            "leftOperand": {
+                                "Variable": {
+                                    "dollar": {
+                                        "kind": "DollarToken",
+                                        "textLength": 1
+                                    },
+                                    "name": {
+                                        "BracedExpression": {
+                                            "openBrace": {
+                                                "kind": "OpenBraceToken",
+                                                "textLength": 1
+                                            },
+                                            "expression": {
+                                                "QualifiedName": {
+                                                    "globalSpecifier": null,
+                                                    "relativeSpecifier": null,
+                                                    "nameParts": [
+                                                        {
+                                                            "kind": "Name",
+                                                            "textLength": 7
+                                                        }
+                                                    ]
+                                                }
+                                            },
+                                            "closeBrace": {
+                                                "kind": "CloseBraceToken",
+                                                "textLength": 1
+                                            }
+                                        }
+                                    }
+                                }
+                            },
+                            "operator": {
+                                "kind": "EqualsToken",
+                                "textLength": 1
+                            },
+                            "byRef": null,
+                            "rightOperand": {
+                                "NumericLiteral": {
+                                    "children": {
+                                        "kind": "IntegerLiteralToken",
+                                        "textLength": 3
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "semicolon": {
+                        "kind": "SemicolonToken",
+                        "textLength": 1
+                    }
+                }
+            },
+            {
+                "error": "SkippedToken",
+                "kind": "CloseBraceToken",
+                "textLength": 1
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/propertyDeclaration2.php.tree b/tests/cases/parser/propertyDeclaration2.php.tree
index 82305b61..2e14fd13 100644
--- a/tests/cases/parser/propertyDeclaration2.php.tree
+++ b/tests/cases/parser/propertyDeclaration2.php.tree
@@ -94,6 +94,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/propertyDeclaration3.php.tree b/tests/cases/parser/propertyDeclaration3.php.tree
index 80a2ba7f..44b1a8b9 100644
--- a/tests/cases/parser/propertyDeclaration3.php.tree
+++ b/tests/cases/parser/propertyDeclaration3.php.tree
@@ -85,6 +85,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/propertyDeclaration4.php.tree b/tests/cases/parser/propertyDeclaration4.php.tree
index 01ebd6e5..6b93af00 100644
--- a/tests/cases/parser/propertyDeclaration4.php.tree
+++ b/tests/cases/parser/propertyDeclaration4.php.tree
@@ -80,6 +80,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/propertyDeclaration5.php.tree b/tests/cases/parser/propertyDeclaration5.php.tree
index e7674763..048d34a5 100644
--- a/tests/cases/parser/propertyDeclaration5.php.tree
+++ b/tests/cases/parser/propertyDeclaration5.php.tree
@@ -132,6 +132,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/propertyDeclaration8.php b/tests/cases/parser/propertyDeclaration8.php
new file mode 100644
index 00000000..a28df9d1
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration8.php
@@ -0,0 +1,4 @@
+<?php
+class A {
+    public $a === 123;
+}
diff --git a/tests/cases/parser/propertyDeclaration8.php.diag b/tests/cases/parser/propertyDeclaration8.php.diag
new file mode 100644
index 00000000..5ce0fdcf
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration8.php.diag
@@ -0,0 +1,26 @@
+[
+    {
+        "kind": 0,
+        "message": "';' expected.",
+        "start": 29,
+        "length": 0
+    },
+    {
+        "kind": 0,
+        "message": "Unexpected '==='",
+        "start": 30,
+        "length": 3
+    },
+    {
+        "kind": 0,
+        "message": "'}' expected.",
+        "start": 33,
+        "length": 0
+    },
+    {
+        "kind": 0,
+        "message": "Unexpected '}'",
+        "start": 39,
+        "length": 1
+    }
+]
\ No newline at end of file
diff --git a/tests/cases/parser/propertyDeclaration8.php.tree b/tests/cases/parser/propertyDeclaration8.php.tree
new file mode 100644
index 00000000..3a014083
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration8.php.tree
@@ -0,0 +1,112 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 1
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": null,
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "Variable": {
+                                                            "dollar": null,
+                                                            "name": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 2
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": null,
+                                        "semicolon": {
+                                            "error": "MissingToken",
+                                            "kind": "SemicolonToken",
+                                            "textLength": 0
+                                        }
+                                    }
+                                },
+                                {
+                                    "error": "SkippedToken",
+                                    "kind": "EqualsEqualsEqualsToken",
+                                    "textLength": 3
+                                }
+                            ],
+                            "closeBrace": {
+                                "error": "MissingToken",
+                                "kind": "CloseBraceToken",
+                                "textLength": 0
+                            }
+                        }
+                    }
+                }
+            },
+            {
+                "ExpressionStatement": {
+                    "expression": {
+                        "NumericLiteral": {
+                            "children": {
+                                "kind": "IntegerLiteralToken",
+                                "textLength": 3
+                            }
+                        }
+                    },
+                    "semicolon": {
+                        "kind": "SemicolonToken",
+                        "textLength": 1
+                    }
+                }
+            },
+            {
+                "error": "SkippedToken",
+                "kind": "CloseBraceToken",
+                "textLength": 1
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/propertyDeclaration9.php b/tests/cases/parser/propertyDeclaration9.php
new file mode 100644
index 00000000..5f1b99da
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration9.php
@@ -0,0 +1,4 @@
+<?php
+class A {
+    public $x = === 123;
+}
diff --git a/tests/cases/parser/propertyDeclaration9.php.diag b/tests/cases/parser/propertyDeclaration9.php.diag
new file mode 100644
index 00000000..15c18265
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration9.php.diag
@@ -0,0 +1,8 @@
+[
+    {
+        "kind": 0,
+        "message": "'Expression' expected.",
+        "start": 31,
+        "length": 0
+    }
+]
\ No newline at end of file
diff --git a/tests/cases/parser/propertyDeclaration9.php.tree b/tests/cases/parser/propertyDeclaration9.php.tree
new file mode 100644
index 00000000..c4dc18b0
--- /dev/null
+++ b/tests/cases/parser/propertyDeclaration9.php.tree
@@ -0,0 +1,114 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 1
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": null,
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "AssignmentExpression": {
+                                                            "leftOperand": {
+                                                                "Variable": {
+                                                                    "dollar": null,
+                                                                    "name": {
+                                                                        "kind": "VariableName",
+                                                                        "textLength": 2
+                                                                    }
+                                                                }
+                                                            },
+                                                            "operator": {
+                                                                "kind": "EqualsToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "byRef": null,
+                                                            "rightOperand": {
+                                                                "BinaryExpression": {
+                                                                    "leftOperand": {
+                                                                        "error": "MissingToken",
+                                                                        "kind": "Expression",
+                                                                        "textLength": 0
+                                                                    },
+                                                                    "operator": {
+                                                                        "kind": "EqualsEqualsEqualsToken",
+                                                                        "textLength": 3
+                                                                    },
+                                                                    "rightOperand": {
+                                                                        "NumericLiteral": {
+                                                                            "children": {
+                                                                                "kind": "IntegerLiteralToken",
+                                                                                "textLength": 3
+                                                                            }
+                                                                        }
+                                                                    }
+                                                                }
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": null,
+                                        "semicolon": {
+                                            "kind": "SemicolonToken",
+                                            "textLength": 1
+                                        }
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/propertyHook1.php b/tests/cases/parser/propertyHook1.php
new file mode 100644
index 00000000..6bd329f7
--- /dev/null
+++ b/tests/cases/parser/propertyHook1.php
@@ -0,0 +1,4 @@
+<?php
+class NoHooks {
+    public $x {}
+}
diff --git a/tests/cases/parser/propertyHook1.php.diag b/tests/cases/parser/propertyHook1.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser/propertyHook1.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser/propertyHook1.php.tree b/tests/cases/parser/propertyHook1.php.tree
new file mode 100644
index 00000000..1e28d9c7
--- /dev/null
+++ b/tests/cases/parser/propertyHook1.php.tree
@@ -0,0 +1,93 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 7
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": null,
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "Variable": {
+                                                            "dollar": null,
+                                                            "name": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 2
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": {
+                                            "PropertyHooks": {
+                                                "openBrace": {
+                                                    "kind": "OpenBraceToken",
+                                                    "textLength": 1
+                                                },
+                                                "hookDeclarations": [],
+                                                "closeBrace": {
+                                                    "kind": "CloseBraceToken",
+                                                    "textLength": 1
+                                                }
+                                            }
+                                        },
+                                        "semicolon": null
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/propertyHook2.php b/tests/cases/parser/propertyHook2.php
new file mode 100644
index 00000000..8694b039
--- /dev/null
+++ b/tests/cases/parser/propertyHook2.php
@@ -0,0 +1,12 @@
+<?php
+class Foo
+{
+    private string $_baz;
+
+    public string $baz {
+        &get => $this->_baz;
+        set {
+            $this->_baz = strtoupper($value);
+        }
+    }
+}
diff --git a/tests/cases/parser/propertyHook2.php.diag b/tests/cases/parser/propertyHook2.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser/propertyHook2.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser/propertyHook2.php.tree b/tests/cases/parser/propertyHook2.php.tree
new file mode 100644
index 00000000..d857fe2f
--- /dev/null
+++ b/tests/cases/parser/propertyHook2.php.tree
@@ -0,0 +1,303 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 3
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PrivateKeyword",
+                                                "textLength": 7
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": {
+                                            "QualifiedNameList": {
+                                                "children": [
+                                                    {
+                                                        "kind": "StringReservedWord",
+                                                        "textLength": 6
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "Variable": {
+                                                            "dollar": null,
+                                                            "name": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 5
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": null,
+                                        "semicolon": {
+                                            "kind": "SemicolonToken",
+                                            "textLength": 1
+                                        }
+                                    }
+                                },
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": {
+                                            "QualifiedNameList": {
+                                                "children": [
+                                                    {
+                                                        "kind": "StringReservedWord",
+                                                        "textLength": 6
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "Variable": {
+                                                            "dollar": null,
+                                                            "name": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 4
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": {
+                                            "PropertyHooks": {
+                                                "openBrace": {
+                                                    "kind": "OpenBraceToken",
+                                                    "textLength": 1
+                                                },
+                                                "hookDeclarations": [
+                                                    {
+                                                        "PropertyHook": {
+                                                            "attributes": null,
+                                                            "byRefToken": {
+                                                                "kind": "AmpersandToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "name": {
+                                                                "kind": "Name",
+                                                                "textLength": 3
+                                                            },
+                                                            "openParen": null,
+                                                            "parameters": null,
+                                                            "closeParen": null,
+                                                            "arrowToken": {
+                                                                "kind": "DoubleArrowToken",
+                                                                "textLength": 2
+                                                            },
+                                                            "body": {
+                                                                "MemberAccessExpression": {
+                                                                    "dereferencableExpression": {
+                                                                        "Variable": {
+                                                                            "dollar": null,
+                                                                            "name": {
+                                                                                "kind": "VariableName",
+                                                                                "textLength": 5
+                                                                            }
+                                                                        }
+                                                                    },
+                                                                    "arrowToken": {
+                                                                        "kind": "ArrowToken",
+                                                                        "textLength": 2
+                                                                    },
+                                                                    "memberName": {
+                                                                        "kind": "Name",
+                                                                        "textLength": 4
+                                                                    }
+                                                                }
+                                                            },
+                                                            "semicolon": {
+                                                                "kind": "SemicolonToken",
+                                                                "textLength": 1
+                                                            }
+                                                        }
+                                                    },
+                                                    {
+                                                        "PropertyHook": {
+                                                            "attributes": null,
+                                                            "byRefToken": null,
+                                                            "name": {
+                                                                "kind": "Name",
+                                                                "textLength": 3
+                                                            },
+                                                            "openParen": null,
+                                                            "parameters": null,
+                                                            "closeParen": null,
+                                                            "arrowToken": null,
+                                                            "body": {
+                                                                "CompoundStatementNode": {
+                                                                    "openBrace": {
+                                                                        "kind": "OpenBraceToken",
+                                                                        "textLength": 1
+                                                                    },
+                                                                    "statements": [
+                                                                        {
+                                                                            "ExpressionStatement": {
+                                                                                "expression": {
+                                                                                    "AssignmentExpression": {
+                                                                                        "leftOperand": {
+                                                                                            "MemberAccessExpression": {
+                                                                                                "dereferencableExpression": {
+                                                                                                    "Variable": {
+                                                                                                        "dollar": null,
+                                                                                                        "name": {
+                                                                                                            "kind": "VariableName",
+                                                                                                            "textLength": 5
+                                                                                                        }
+                                                                                                    }
+                                                                                                },
+                                                                                                "arrowToken": {
+                                                                                                    "kind": "ArrowToken",
+                                                                                                    "textLength": 2
+                                                                                                },
+                                                                                                "memberName": {
+                                                                                                    "kind": "Name",
+                                                                                                    "textLength": 4
+                                                                                                }
+                                                                                            }
+                                                                                        },
+                                                                                        "operator": {
+                                                                                            "kind": "EqualsToken",
+                                                                                            "textLength": 1
+                                                                                        },
+                                                                                        "byRef": null,
+                                                                                        "rightOperand": {
+                                                                                            "CallExpression": {
+                                                                                                "callableExpression": {
+                                                                                                    "QualifiedName": {
+                                                                                                        "globalSpecifier": null,
+                                                                                                        "relativeSpecifier": null,
+                                                                                                        "nameParts": [
+                                                                                                            {
+                                                                                                                "kind": "Name",
+                                                                                                                "textLength": 10
+                                                                                                            }
+                                                                                                        ]
+                                                                                                    }
+                                                                                                },
+                                                                                                "openParen": {
+                                                                                                    "kind": "OpenParenToken",
+                                                                                                    "textLength": 1
+                                                                                                },
+                                                                                                "argumentExpressionList": {
+                                                                                                    "ArgumentExpressionList": {
+                                                                                                        "children": [
+                                                                                                            {
+                                                                                                                "ArgumentExpression": {
+                                                                                                                    "name": null,
+                                                                                                                    "colonToken": null,
+                                                                                                                    "dotDotDotToken": null,
+                                                                                                                    "expression": {
+                                                                                                                        "Variable": {
+                                                                                                                            "dollar": null,
+                                                                                                                            "name": {
+                                                                                                                                "kind": "VariableName",
+                                                                                                                                "textLength": 6
+                                                                                                                            }
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            }
+                                                                                                        ]
+                                                                                                    }
+                                                                                                },
+                                                                                                "closeParen": {
+                                                                                                    "kind": "CloseParenToken",
+                                                                                                    "textLength": 1
+                                                                                                }
+                                                                                            }
+                                                                                        }
+                                                                                    }
+                                                                                },
+                                                                                "semicolon": {
+                                                                                    "kind": "SemicolonToken",
+                                                                                    "textLength": 1
+                                                                                }
+                                                                            }
+                                                                        }
+                                                                    ],
+                                                                    "closeBrace": {
+                                                                        "kind": "CloseBraceToken",
+                                                                        "textLength": 1
+                                                                    }
+                                                                }
+                                                            },
+                                                            "semicolon": null
+                                                        }
+                                                    }
+                                                ],
+                                                "closeBrace": {
+                                                    "kind": "CloseBraceToken",
+                                                    "textLength": 1
+                                                }
+                                            }
+                                        },
+                                        "semicolon": null
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/propertyHook3.php b/tests/cases/parser/propertyHook3.php
new file mode 100644
index 00000000..d06352ec
--- /dev/null
+++ b/tests/cases/parser/propertyHook3.php
@@ -0,0 +1,15 @@
+<?php
+
+class User
+{
+    public function __construct(public string $first, public string $last) {}
+
+    public string $fullName {
+        get {
+            return "$this->first $this->last";
+        }
+        set(string $value) {
+            [$this->first, $this->last] = explode(' ', $value, 2);
+        }
+    }
+}
diff --git a/tests/cases/parser/propertyHook3.php.diag b/tests/cases/parser/propertyHook3.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser/propertyHook3.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser/propertyHook3.php.tree b/tests/cases/parser/propertyHook3.php.tree
new file mode 100644
index 00000000..5436f03b
--- /dev/null
+++ b/tests/cases/parser/propertyHook3.php.tree
@@ -0,0 +1,569 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 4
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "MethodDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "functionKeyword": {
+                                            "kind": "FunctionKeyword",
+                                            "textLength": 8
+                                        },
+                                        "byRefToken": null,
+                                        "name": {
+                                            "kind": "Name",
+                                            "textLength": 11
+                                        },
+                                        "openParen": {
+                                            "kind": "OpenParenToken",
+                                            "textLength": 1
+                                        },
+                                        "parameters": {
+                                            "ParameterDeclarationList": {
+                                                "children": [
+                                                    {
+                                                        "Parameter": {
+                                                            "attributes": null,
+                                                            "visibilityToken": {
+                                                                "kind": "PublicKeyword",
+                                                                "textLength": 6
+                                                            },
+                                                            "modifiers": null,
+                                                            "questionToken": null,
+                                                            "typeDeclarationList": {
+                                                                "QualifiedNameList": {
+                                                                    "children": [
+                                                                        {
+                                                                            "kind": "StringReservedWord",
+                                                                            "textLength": 6
+                                                                        }
+                                                                    ]
+                                                                }
+                                                            },
+                                                            "byRefToken": null,
+                                                            "dotDotDotToken": null,
+                                                            "variableName": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 6
+                                                            },
+                                                            "equalsToken": null,
+                                                            "default": null,
+                                                            "propertyHooks": null
+                                                        }
+                                                    },
+                                                    {
+                                                        "kind": "CommaToken",
+                                                        "textLength": 1
+                                                    },
+                                                    {
+                                                        "Parameter": {
+                                                            "attributes": null,
+                                                            "visibilityToken": {
+                                                                "kind": "PublicKeyword",
+                                                                "textLength": 6
+                                                            },
+                                                            "modifiers": null,
+                                                            "questionToken": null,
+                                                            "typeDeclarationList": {
+                                                                "QualifiedNameList": {
+                                                                    "children": [
+                                                                        {
+                                                                            "kind": "StringReservedWord",
+                                                                            "textLength": 6
+                                                                        }
+                                                                    ]
+                                                                }
+                                                            },
+                                                            "byRefToken": null,
+                                                            "dotDotDotToken": null,
+                                                            "variableName": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 5
+                                                            },
+                                                            "equalsToken": null,
+                                                            "default": null,
+                                                            "propertyHooks": null
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "closeParen": {
+                                            "kind": "CloseParenToken",
+                                            "textLength": 1
+                                        },
+                                        "colonToken": null,
+                                        "questionToken": null,
+                                        "returnTypeList": null,
+                                        "compoundStatementOrSemicolon": {
+                                            "CompoundStatementNode": {
+                                                "openBrace": {
+                                                    "kind": "OpenBraceToken",
+                                                    "textLength": 1
+                                                },
+                                                "statements": [],
+                                                "closeBrace": {
+                                                    "kind": "CloseBraceToken",
+                                                    "textLength": 1
+                                                }
+                                            }
+                                        }
+                                    }
+                                },
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": {
+                                            "QualifiedNameList": {
+                                                "children": [
+                                                    {
+                                                        "kind": "StringReservedWord",
+                                                        "textLength": 6
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "Variable": {
+                                                            "dollar": null,
+                                                            "name": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 9
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": {
+                                            "PropertyHooks": {
+                                                "openBrace": {
+                                                    "kind": "OpenBraceToken",
+                                                    "textLength": 1
+                                                },
+                                                "hookDeclarations": [
+                                                    {
+                                                        "PropertyHook": {
+                                                            "attributes": null,
+                                                            "byRefToken": null,
+                                                            "name": {
+                                                                "kind": "Name",
+                                                                "textLength": 3
+                                                            },
+                                                            "openParen": null,
+                                                            "parameters": null,
+                                                            "closeParen": null,
+                                                            "arrowToken": null,
+                                                            "body": {
+                                                                "CompoundStatementNode": {
+                                                                    "openBrace": {
+                                                                        "kind": "OpenBraceToken",
+                                                                        "textLength": 1
+                                                                    },
+                                                                    "statements": [
+                                                                        {
+                                                                            "ReturnStatement": {
+                                                                                "returnKeyword": {
+                                                                                    "kind": "ReturnKeyword",
+                                                                                    "textLength": 6
+                                                                                },
+                                                                                "expression": {
+                                                                                    "StringLiteral": {
+                                                                                        "startQuote": {
+                                                                                            "kind": "DoubleQuoteToken",
+                                                                                            "textLength": 1
+                                                                                        },
+                                                                                        "children": [
+                                                                                            {
+                                                                                                "MemberAccessExpression": {
+                                                                                                    "dereferencableExpression": {
+                                                                                                        "Variable": {
+                                                                                                            "dollar": null,
+                                                                                                            "name": {
+                                                                                                                "kind": "VariableName",
+                                                                                                                "textLength": 5
+                                                                                                            }
+                                                                                                        }
+                                                                                                    },
+                                                                                                    "arrowToken": {
+                                                                                                        "kind": "ArrowToken",
+                                                                                                        "textLength": 2
+                                                                                                    },
+                                                                                                    "memberName": {
+                                                                                                        "kind": "Name",
+                                                                                                        "textLength": 5
+                                                                                                    }
+                                                                                                }
+                                                                                            },
+                                                                                            {
+                                                                                                "kind": "EncapsedAndWhitespace",
+                                                                                                "textLength": 1
+                                                                                            },
+                                                                                            {
+                                                                                                "MemberAccessExpression": {
+                                                                                                    "dereferencableExpression": {
+                                                                                                        "Variable": {
+                                                                                                            "dollar": null,
+                                                                                                            "name": {
+                                                                                                                "kind": "VariableName",
+                                                                                                                "textLength": 5
+                                                                                                            }
+                                                                                                        }
+                                                                                                    },
+                                                                                                    "arrowToken": {
+                                                                                                        "kind": "ArrowToken",
+                                                                                                        "textLength": 2
+                                                                                                    },
+                                                                                                    "memberName": {
+                                                                                                        "kind": "Name",
+                                                                                                        "textLength": 4
+                                                                                                    }
+                                                                                                }
+                                                                                            }
+                                                                                        ],
+                                                                                        "endQuote": {
+                                                                                            "kind": "DoubleQuoteToken",
+                                                                                            "textLength": 1
+                                                                                        }
+                                                                                    }
+                                                                                },
+                                                                                "semicolon": {
+                                                                                    "kind": "SemicolonToken",
+                                                                                    "textLength": 1
+                                                                                }
+                                                                            }
+                                                                        }
+                                                                    ],
+                                                                    "closeBrace": {
+                                                                        "kind": "CloseBraceToken",
+                                                                        "textLength": 1
+                                                                    }
+                                                                }
+                                                            },
+                                                            "semicolon": null
+                                                        }
+                                                    },
+                                                    {
+                                                        "PropertyHook": {
+                                                            "attributes": null,
+                                                            "byRefToken": null,
+                                                            "name": {
+                                                                "kind": "Name",
+                                                                "textLength": 3
+                                                            },
+                                                            "openParen": {
+                                                                "kind": "OpenParenToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "parameters": {
+                                                                "ParameterDeclarationList": {
+                                                                    "children": [
+                                                                        {
+                                                                            "Parameter": {
+                                                                                "attributes": null,
+                                                                                "visibilityToken": null,
+                                                                                "modifiers": null,
+                                                                                "questionToken": null,
+                                                                                "typeDeclarationList": {
+                                                                                    "QualifiedNameList": {
+                                                                                        "children": [
+                                                                                            {
+                                                                                                "kind": "StringReservedWord",
+                                                                                                "textLength": 6
+                                                                                            }
+                                                                                        ]
+                                                                                    }
+                                                                                },
+                                                                                "byRefToken": null,
+                                                                                "dotDotDotToken": null,
+                                                                                "variableName": {
+                                                                                    "kind": "VariableName",
+                                                                                    "textLength": 6
+                                                                                },
+                                                                                "equalsToken": null,
+                                                                                "default": null,
+                                                                                "propertyHooks": null
+                                                                            }
+                                                                        }
+                                                                    ]
+                                                                }
+                                                            },
+                                                            "closeParen": {
+                                                                "kind": "CloseParenToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "arrowToken": null,
+                                                            "body": {
+                                                                "CompoundStatementNode": {
+                                                                    "openBrace": {
+                                                                        "kind": "OpenBraceToken",
+                                                                        "textLength": 1
+                                                                    },
+                                                                    "statements": [
+                                                                        {
+                                                                            "ExpressionStatement": {
+                                                                                "expression": {
+                                                                                    "AssignmentExpression": {
+                                                                                        "leftOperand": {
+                                                                                            "ArrayCreationExpression": {
+                                                                                                "arrayKeyword": null,
+                                                                                                "openParenOrBracket": {
+                                                                                                    "kind": "OpenBracketToken",
+                                                                                                    "textLength": 1
+                                                                                                },
+                                                                                                "arrayElements": {
+                                                                                                    "ArrayElementList": {
+                                                                                                        "children": [
+                                                                                                            {
+                                                                                                                "ArrayElement": {
+                                                                                                                    "elementKey": null,
+                                                                                                                    "arrowToken": null,
+                                                                                                                    "byRef": null,
+                                                                                                                    "dotDotDot": null,
+                                                                                                                    "elementValue": {
+                                                                                                                        "MemberAccessExpression": {
+                                                                                                                            "dereferencableExpression": {
+                                                                                                                                "Variable": {
+                                                                                                                                    "dollar": null,
+                                                                                                                                    "name": {
+                                                                                                                                        "kind": "VariableName",
+                                                                                                                                        "textLength": 5
+                                                                                                                                    }
+                                                                                                                                }
+                                                                                                                            },
+                                                                                                                            "arrowToken": {
+                                                                                                                                "kind": "ArrowToken",
+                                                                                                                                "textLength": 2
+                                                                                                                            },
+                                                                                                                            "memberName": {
+                                                                                                                                "kind": "Name",
+                                                                                                                                "textLength": 5
+                                                                                                                            }
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "kind": "CommaToken",
+                                                                                                                "textLength": 1
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "ArrayElement": {
+                                                                                                                    "elementKey": null,
+                                                                                                                    "arrowToken": null,
+                                                                                                                    "byRef": null,
+                                                                                                                    "dotDotDot": null,
+                                                                                                                    "elementValue": {
+                                                                                                                        "MemberAccessExpression": {
+                                                                                                                            "dereferencableExpression": {
+                                                                                                                                "Variable": {
+                                                                                                                                    "dollar": null,
+                                                                                                                                    "name": {
+                                                                                                                                        "kind": "VariableName",
+                                                                                                                                        "textLength": 5
+                                                                                                                                    }
+                                                                                                                                }
+                                                                                                                            },
+                                                                                                                            "arrowToken": {
+                                                                                                                                "kind": "ArrowToken",
+                                                                                                                                "textLength": 2
+                                                                                                                            },
+                                                                                                                            "memberName": {
+                                                                                                                                "kind": "Name",
+                                                                                                                                "textLength": 4
+                                                                                                                            }
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            }
+                                                                                                        ]
+                                                                                                    }
+                                                                                                },
+                                                                                                "closeParenOrBracket": {
+                                                                                                    "kind": "CloseBracketToken",
+                                                                                                    "textLength": 1
+                                                                                                }
+                                                                                            }
+                                                                                        },
+                                                                                        "operator": {
+                                                                                            "kind": "EqualsToken",
+                                                                                            "textLength": 1
+                                                                                        },
+                                                                                        "byRef": null,
+                                                                                        "rightOperand": {
+                                                                                            "CallExpression": {
+                                                                                                "callableExpression": {
+                                                                                                    "QualifiedName": {
+                                                                                                        "globalSpecifier": null,
+                                                                                                        "relativeSpecifier": null,
+                                                                                                        "nameParts": [
+                                                                                                            {
+                                                                                                                "kind": "Name",
+                                                                                                                "textLength": 7
+                                                                                                            }
+                                                                                                        ]
+                                                                                                    }
+                                                                                                },
+                                                                                                "openParen": {
+                                                                                                    "kind": "OpenParenToken",
+                                                                                                    "textLength": 1
+                                                                                                },
+                                                                                                "argumentExpressionList": {
+                                                                                                    "ArgumentExpressionList": {
+                                                                                                        "children": [
+                                                                                                            {
+                                                                                                                "ArgumentExpression": {
+                                                                                                                    "name": null,
+                                                                                                                    "colonToken": null,
+                                                                                                                    "dotDotDotToken": null,
+                                                                                                                    "expression": {
+                                                                                                                        "StringLiteral": {
+                                                                                                                            "startQuote": null,
+                                                                                                                            "children": {
+                                                                                                                                "kind": "StringLiteralToken",
+                                                                                                                                "textLength": 3
+                                                                                                                            },
+                                                                                                                            "endQuote": null
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "kind": "CommaToken",
+                                                                                                                "textLength": 1
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "ArgumentExpression": {
+                                                                                                                    "name": null,
+                                                                                                                    "colonToken": null,
+                                                                                                                    "dotDotDotToken": null,
+                                                                                                                    "expression": {
+                                                                                                                        "Variable": {
+                                                                                                                            "dollar": null,
+                                                                                                                            "name": {
+                                                                                                                                "kind": "VariableName",
+                                                                                                                                "textLength": 6
+                                                                                                                            }
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "kind": "CommaToken",
+                                                                                                                "textLength": 1
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "ArgumentExpression": {
+                                                                                                                    "name": null,
+                                                                                                                    "colonToken": null,
+                                                                                                                    "dotDotDotToken": null,
+                                                                                                                    "expression": {
+                                                                                                                        "NumericLiteral": {
+                                                                                                                            "children": {
+                                                                                                                                "kind": "IntegerLiteralToken",
+                                                                                                                                "textLength": 1
+                                                                                                                            }
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            }
+                                                                                                        ]
+                                                                                                    }
+                                                                                                },
+                                                                                                "closeParen": {
+                                                                                                    "kind": "CloseParenToken",
+                                                                                                    "textLength": 1
+                                                                                                }
+                                                                                            }
+                                                                                        }
+                                                                                    }
+                                                                                },
+                                                                                "semicolon": {
+                                                                                    "kind": "SemicolonToken",
+                                                                                    "textLength": 1
+                                                                                }
+                                                                            }
+                                                                        }
+                                                                    ],
+                                                                    "closeBrace": {
+                                                                        "kind": "CloseBraceToken",
+                                                                        "textLength": 1
+                                                                    }
+                                                                }
+                                                            },
+                                                            "semicolon": null
+                                                        }
+                                                    }
+                                                ],
+                                                "closeBrace": {
+                                                    "kind": "CloseBraceToken",
+                                                    "textLength": 1
+                                                }
+                                            }
+                                        },
+                                        "semicolon": null
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser/true.php.tree b/tests/cases/parser/true.php.tree
index 4e4cfe2c..24669863 100644
--- a/tests/cases/parser/true.php.tree
+++ b/tests/cases/parser/true.php.tree
@@ -53,7 +53,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/typedProperties.php.tree b/tests/cases/parser/typedProperties.php.tree
index 00286e48..3aa68e99 100644
--- a/tests/cases/parser/typedProperties.php.tree
+++ b/tests/cases/parser/typedProperties.php.tree
@@ -92,6 +92,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
@@ -141,6 +142,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
@@ -205,6 +207,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
@@ -265,6 +268,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/typedPropertiesNoType.php.tree b/tests/cases/parser/typedPropertiesNoType.php.tree
index b794cc28..a3dfbdc5 100644
--- a/tests/cases/parser/typedPropertiesNoType.php.tree
+++ b/tests/cases/parser/typedPropertiesNoType.php.tree
@@ -83,6 +83,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/typedPropertiesSimple.php.tree b/tests/cases/parser/typedPropertiesSimple.php.tree
index da0973a5..19961159 100644
--- a/tests/cases/parser/typedPropertiesSimple.php.tree
+++ b/tests/cases/parser/typedPropertiesSimple.php.tree
@@ -85,6 +85,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/unionTypeParameter1.php.tree b/tests/cases/parser/unionTypeParameter1.php.tree
index b1ed34cf..aeec322c 100644
--- a/tests/cases/parser/unionTypeParameter1.php.tree
+++ b/tests/cases/parser/unionTypeParameter1.php.tree
@@ -61,7 +61,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/unionTypeParameter2.php.tree b/tests/cases/parser/unionTypeParameter2.php.tree
index 11a5b87e..aee37913 100644
--- a/tests/cases/parser/unionTypeParameter2.php.tree
+++ b/tests/cases/parser/unionTypeParameter2.php.tree
@@ -107,7 +107,8 @@
                                                     "textLength": 4
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
diff --git a/tests/cases/parser/unionTypeProperty.php.tree b/tests/cases/parser/unionTypeProperty.php.tree
index e55f841b..7abaee00 100644
--- a/tests/cases/parser/unionTypeProperty.php.tree
+++ b/tests/cases/parser/unionTypeProperty.php.tree
@@ -84,6 +84,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
@@ -183,6 +184,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/unionTypeProperty2.php.tree b/tests/cases/parser/unionTypeProperty2.php.tree
index 2ecf8975..3c6ab566 100644
--- a/tests/cases/parser/unionTypeProperty2.php.tree
+++ b/tests/cases/parser/unionTypeProperty2.php.tree
@@ -174,6 +174,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser/yieldExpression18.php.tree b/tests/cases/parser/yieldExpression18.php.tree
index f0cd6119..fef7a725 100644
--- a/tests/cases/parser/yieldExpression18.php.tree
+++ b/tests/cases/parser/yieldExpression18.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser/yieldExpression19.php.tree b/tests/cases/parser/yieldExpression19.php.tree
index 1c02f0c1..ca76eb78 100644
--- a/tests/cases/parser/yieldExpression19.php.tree
+++ b/tests/cases/parser/yieldExpression19.php.tree
@@ -44,7 +44,8 @@
                                             "textLength": 2
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser74/arrowFunction.php.tree b/tests/cases/parser74/arrowFunction.php.tree
index 0d202395..4ec8281b 100644
--- a/tests/cases/parser74/arrowFunction.php.tree
+++ b/tests/cases/parser74/arrowFunction.php.tree
@@ -69,7 +69,8 @@
                                                             "textLength": 2
                                                         },
                                                         "equalsToken": null,
-                                                        "default": null
+                                                        "default": null,
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
@@ -205,7 +206,8 @@
                                                             "textLength": 2
                                                         }
                                                     }
-                                                }
+                                                },
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
@@ -291,7 +293,8 @@
                                                     "textLength": 2
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
@@ -361,7 +364,8 @@
                                                     "textLength": 2
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
@@ -428,7 +432,8 @@
                                                     "textLength": 2
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         },
                                         {
@@ -452,7 +457,8 @@
                                                     "textLength": 5
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
diff --git a/tests/cases/parser74/arrowFunctionFallback.php.tree b/tests/cases/parser74/arrowFunctionFallback.php.tree
index 3d236338..9e0b731a 100644
--- a/tests/cases/parser74/arrowFunctionFallback.php.tree
+++ b/tests/cases/parser74/arrowFunctionFallback.php.tree
@@ -80,7 +80,8 @@
                                                             "textLength": 2
                                                         },
                                                         "equalsToken": null,
-                                                        "default": null
+                                                        "default": null,
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
@@ -267,7 +268,8 @@
                                                                     "textLength": 2
                                                                 }
                                                             }
-                                                        }
+                                                        },
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
@@ -372,7 +374,8 @@
                                                             "textLength": 2
                                                         },
                                                         "equalsToken": null,
-                                                        "default": null
+                                                        "default": null,
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
@@ -486,7 +489,8 @@
                                                             "textLength": 2
                                                         },
                                                         "equalsToken": null,
-                                                        "default": null
+                                                        "default": null,
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
@@ -603,7 +607,8 @@
                                                             "textLength": 4
                                                         },
                                                         "equalsToken": null,
-                                                        "default": null
+                                                        "default": null,
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
diff --git a/tests/cases/parser74/arrowFunctionTypes.php.tree b/tests/cases/parser74/arrowFunctionTypes.php.tree
index 045202fe..f5f74f49 100644
--- a/tests/cases/parser74/arrowFunctionTypes.php.tree
+++ b/tests/cases/parser74/arrowFunctionTypes.php.tree
@@ -80,7 +80,8 @@
                                                             "textLength": 2
                                                         },
                                                         "equalsToken": null,
-                                                        "default": null
+                                                        "default": null,
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
@@ -282,7 +283,8 @@
                                                                     "textLength": 2
                                                                 }
                                                             }
-                                                        }
+                                                        },
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
@@ -386,7 +388,8 @@
                                                             "textLength": 2
                                                         },
                                                         "equalsToken": null,
-                                                        "default": null
+                                                        "default": null,
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
@@ -499,7 +502,8 @@
                                                             "textLength": 2
                                                         },
                                                         "equalsToken": null,
-                                                        "default": null
+                                                        "default": null,
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
@@ -612,7 +616,8 @@
                                                             "textLength": 4
                                                         },
                                                         "equalsToken": null,
-                                                        "default": null
+                                                        "default": null,
+                                                        "propertyHooks": null
                                                     }
                                                 }
                                             ]
diff --git a/tests/cases/parser74/arrowFunctionUnionTypes.php.tree b/tests/cases/parser74/arrowFunctionUnionTypes.php.tree
index 901721b0..ab6a4306 100644
--- a/tests/cases/parser74/arrowFunctionUnionTypes.php.tree
+++ b/tests/cases/parser74/arrowFunctionUnionTypes.php.tree
@@ -64,7 +64,8 @@
                                                     "textLength": 7
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
@@ -169,7 +170,8 @@
                                                     "textLength": 2
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
@@ -281,7 +283,8 @@
                                                     "textLength": 6
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
@@ -443,7 +446,8 @@
                                                     "textLength": 0
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
diff --git a/tests/cases/parser80/attributes18.php.tree b/tests/cases/parser80/attributes18.php.tree
index 16abce90..ee93aaa9 100644
--- a/tests/cases/parser80/attributes18.php.tree
+++ b/tests/cases/parser80/attributes18.php.tree
@@ -65,7 +65,8 @@
                                                     "textLength": 0
                                                 },
                                                 "equalsToken": null,
-                                                "default": null
+                                                "default": null,
+                                                "propertyHooks": null
                                             }
                                         }
                                     ]
diff --git a/tests/cases/parser80/attributes20.php b/tests/cases/parser80/attributes20.php
new file mode 100644
index 00000000..8711b4b5
--- /dev/null
+++ b/tests/cases/parser80/attributes20.php
@@ -0,0 +1,7 @@
+<?php
+class X {
+    public string $name {
+        #[SomeAttribute]
+        get => 'default';
+    }
+}
diff --git a/tests/cases/parser80/attributes20.php.diag b/tests/cases/parser80/attributes20.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser80/attributes20.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser80/attributes20.php.tree b/tests/cases/parser80/attributes20.php.tree
new file mode 100644
index 00000000..07243c97
--- /dev/null
+++ b/tests/cases/parser80/attributes20.php.tree
@@ -0,0 +1,172 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 1
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": {
+                                            "QualifiedNameList": {
+                                                "children": [
+                                                    {
+                                                        "kind": "StringReservedWord",
+                                                        "textLength": 6
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "Variable": {
+                                                            "dollar": null,
+                                                            "name": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 5
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": {
+                                            "PropertyHooks": {
+                                                "openBrace": {
+                                                    "kind": "OpenBraceToken",
+                                                    "textLength": 1
+                                                },
+                                                "hookDeclarations": [
+                                                    {
+                                                        "PropertyHook": {
+                                                            "attributes": [
+                                                                {
+                                                                    "AttributeGroup": {
+                                                                        "startToken": {
+                                                                            "kind": "AttributeToken",
+                                                                            "textLength": 2
+                                                                        },
+                                                                        "attributes": {
+                                                                            "AttributeElementList": {
+                                                                                "children": [
+                                                                                    {
+                                                                                        "Attribute": {
+                                                                                            "name": {
+                                                                                                "QualifiedName": {
+                                                                                                    "globalSpecifier": null,
+                                                                                                    "relativeSpecifier": null,
+                                                                                                    "nameParts": [
+                                                                                                        {
+                                                                                                            "kind": "Name",
+                                                                                                            "textLength": 13
+                                                                                                        }
+                                                                                                    ]
+                                                                                                }
+                                                                                            },
+                                                                                            "openParen": null,
+                                                                                            "argumentExpressionList": null,
+                                                                                            "closeParen": null
+                                                                                        }
+                                                                                    }
+                                                                                ]
+                                                                            }
+                                                                        },
+                                                                        "endToken": {
+                                                                            "kind": "CloseBracketToken",
+                                                                            "textLength": 1
+                                                                        }
+                                                                    }
+                                                                }
+                                                            ],
+                                                            "byRefToken": null,
+                                                            "name": {
+                                                                "kind": "Name",
+                                                                "textLength": 3
+                                                            },
+                                                            "openParen": null,
+                                                            "parameters": null,
+                                                            "closeParen": null,
+                                                            "arrowToken": {
+                                                                "kind": "DoubleArrowToken",
+                                                                "textLength": 2
+                                                            },
+                                                            "body": {
+                                                                "StringLiteral": {
+                                                                    "startQuote": null,
+                                                                    "children": {
+                                                                        "kind": "StringLiteralToken",
+                                                                        "textLength": 9
+                                                                    },
+                                                                    "endQuote": null
+                                                                }
+                                                            },
+                                                            "semicolon": {
+                                                                "kind": "SemicolonToken",
+                                                                "textLength": 1
+                                                            }
+                                                        }
+                                                    }
+                                                ],
+                                                "closeBrace": {
+                                                    "kind": "CloseBraceToken",
+                                                    "textLength": 1
+                                                }
+                                            }
+                                        },
+                                        "semicolon": null
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser80/attributes4.php.tree b/tests/cases/parser80/attributes4.php.tree
index 6911f240..42818d5e 100644
--- a/tests/cases/parser80/attributes4.php.tree
+++ b/tests/cases/parser80/attributes4.php.tree
@@ -108,6 +108,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser80/attributes5.php.tree b/tests/cases/parser80/attributes5.php.tree
index 4c5f1c18..92102aba 100644
--- a/tests/cases/parser80/attributes5.php.tree
+++ b/tests/cases/parser80/attributes5.php.tree
@@ -82,7 +82,8 @@
                                             "textLength": 6
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
diff --git a/tests/cases/parser80/mixedType1.php.tree b/tests/cases/parser80/mixedType1.php.tree
index ce93968b..858b5e74 100644
--- a/tests/cases/parser80/mixedType1.php.tree
+++ b/tests/cases/parser80/mixedType1.php.tree
@@ -53,7 +53,8 @@
                                             "textLength": 4
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
diff --git a/tests/cases/parser81/intersection_type.php.tree b/tests/cases/parser81/intersection_type.php.tree
index 2be7447a..5ef247ad 100644
--- a/tests/cases/parser81/intersection_type.php.tree
+++ b/tests/cases/parser81/intersection_type.php.tree
@@ -93,7 +93,8 @@
                                             "textLength": 6
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 },
                                 {
@@ -150,7 +151,8 @@
                                             "textLength": 7
                                         },
                                         "equalsToken": null,
-                                        "default": null
+                                        "default": null,
+                                        "propertyHooks": null
                                     }
                                 }
                             ]
@@ -295,6 +297,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
diff --git a/tests/cases/parser81/readonly_property.php.tree b/tests/cases/parser81/readonly_property.php.tree
index c6991256..5426d070 100644
--- a/tests/cases/parser81/readonly_property.php.tree
+++ b/tests/cases/parser81/readonly_property.php.tree
@@ -72,6 +72,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1
@@ -166,6 +167,7 @@
                                                 ]
                                             }
                                         },
+                                        "propertyHooks": null,
                                         "semicolon": {
                                             "kind": "SemicolonToken",
                                             "textLength": 1

From 1492f477843c10f5bb2536a73e416517eba0c162 Mon Sep 17 00:00:00 2001
From: Daniel Leech <daniel@dantleech.com>
Date: Mon, 27 Jan 2025 23:27:51 +0000
Subject: [PATCH 10/11] Make PropertyHook a FunctionLike

---
 src/Node/PropertyHook.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/Node/PropertyHook.php b/src/Node/PropertyHook.php
index 9d2cadf0..9650fc09 100644
--- a/src/Node/PropertyHook.php
+++ b/src/Node/PropertyHook.php
@@ -6,11 +6,12 @@
 
 namespace Microsoft\PhpParser\Node;
 
+use Microsoft\PhpParser\FunctionLike;
 use Microsoft\PhpParser\Node;
 use Microsoft\PhpParser\Node\Statement\CompoundStatementNode;
 use Microsoft\PhpParser\Token;
 
-class PropertyHook extends Node {
+class PropertyHook extends Node implements FunctionLike {
     /** @var AttributeGroup[]|null */
     public $attributes;
     /** @var Token */

From 50911f63a76159a59206fd08e07a6dfa04a1e9a2 Mon Sep 17 00:00:00 2001
From: Daniel Leech <daniel@dantleech.com>
Date: Wed, 5 Feb 2025 22:21:34 +0000
Subject: [PATCH 11/11] Support property hooks default value parsing

These tests are cursed.
---
 src/Node.php                              |   4 +-
 src/Parser.php                            |   6 +-
 tests/cases/lexical/keyword5.php.tokens   |   8 +-
 tests/cases/parser/propertyHook4.php      |  15 +
 tests/cases/parser/propertyHook4.php.diag |   1 +
 tests/cases/parser/propertyHook4.php.tree | 588 ++++++++++++++++++++++
 tests/cases/parser81/enums6.php.diag      |   2 +-
 tests/cases/parser81/enums6.php.tree      |  42 +-
 8 files changed, 635 insertions(+), 31 deletions(-)
 create mode 100644 tests/cases/parser/propertyHook4.php
 create mode 100644 tests/cases/parser/propertyHook4.php.diag
 create mode 100644 tests/cases/parser/propertyHook4.php.tree

diff --git a/src/Node.php b/src/Node.php
index 5de35751..b33bdaeb 100644
--- a/src/Node.php
+++ b/src/Node.php
@@ -341,11 +341,11 @@ public function getFullWidth() : int {
      * Gets string representing Node text (not including leading comment + whitespace trivia)
      * @return string
      */
-    public function getText() : string {
+    public function getText(?string $fileContents= null) : string {
         $start = $this->getStartPosition();
         $end = $this->getEndPosition();
 
-        $fileContents = $this->getFileContents();
+        $fileContents = $fileContents ?? $this->getFileContents();
         return \substr($fileContents, $start, $end - $start);
     }
 
diff --git a/src/Parser.php b/src/Parser.php
index dba052e1..0ffc0d16 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -2192,6 +2192,7 @@ private function parseBinaryExpressionOrHigher($precedence, $parentNode) {
             // the original operator, and the newly constructed exponentiation-expression as the operand.
             $shouldOperatorTakePrecedenceOverUnary = false;
             switch ($token->kind) {
+                case TokenKind::OpenBraceToken:
                 case TokenKind::AsteriskAsteriskToken:
                     $shouldOperatorTakePrecedenceOverUnary = $leftOperand instanceof UnaryExpression;
                     break;
@@ -3056,6 +3057,9 @@ private function parsePostfixExpressionRest($expression, $allowUpdateExpression
         )) {
             return $expression;
         }
+        if ($tokenKind === TokenKind::OpenBraceToken) {
+            return $expression;
+        }
         if ($tokenKind === TokenKind::ColonColonToken) {
             $expression = $this->parseScopedPropertyAccessExpression($expression, null);
             return $this->parsePostfixExpressionRest($expression);
@@ -3462,7 +3466,7 @@ private function parsePropertyNameList($parentNode) {
         return $this->parseDelimitedList(
             DelimitedList\ExpressionList::class,
             TokenKind::CommaToken,
-            function ($token) {
+            function (Token $token) {
                 return $token->kind === TokenKind::VariableName;
             },
             $this->parsePropertyVariableNameAndDefault(),
diff --git a/tests/cases/lexical/keyword5.php.tokens b/tests/cases/lexical/keyword5.php.tokens
index 7186038d..07728cfa 100644
--- a/tests/cases/lexical/keyword5.php.tokens
+++ b/tests/cases/lexical/keyword5.php.tokens
@@ -4,12 +4,8 @@
         "textLength": 6
     },
     {
-        "kind": "YieldKeyword",
-        "textLength": 5
-    },
-    {
-        "kind": "Name",
-        "textLength": 4
+        "kind": "YieldFromKeyword",
+        "textLength": 22
     },
     {
         "kind": "EndOfFileToken",
diff --git a/tests/cases/parser/propertyHook4.php b/tests/cases/parser/propertyHook4.php
new file mode 100644
index 00000000..fb011b49
--- /dev/null
+++ b/tests/cases/parser/propertyHook4.php
@@ -0,0 +1,15 @@
+<?php
+
+class User
+{
+    public function __construct(public string $first, public string $last) {}
+
+    public string $fullName = 'hello world' {
+        get {
+            return "$this->first $this->last";
+        }
+        set(string $value) {
+            [$this->first, $this->last] = explode(' ', $value, 2);
+        }
+    }
+}
diff --git a/tests/cases/parser/propertyHook4.php.diag b/tests/cases/parser/propertyHook4.php.diag
new file mode 100644
index 00000000..0637a088
--- /dev/null
+++ b/tests/cases/parser/propertyHook4.php.diag
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/tests/cases/parser/propertyHook4.php.tree b/tests/cases/parser/propertyHook4.php.tree
new file mode 100644
index 00000000..d8631ff0
--- /dev/null
+++ b/tests/cases/parser/propertyHook4.php.tree
@@ -0,0 +1,588 @@
+{
+    "SourceFileNode": {
+        "statementList": [
+            {
+                "InlineHtml": {
+                    "scriptSectionEndTag": null,
+                    "text": null,
+                    "scriptSectionStartTag": {
+                        "kind": "ScriptSectionStartTag",
+                        "textLength": 6
+                    }
+                }
+            },
+            {
+                "ClassDeclaration": {
+                    "attributes": null,
+                    "abstractOrFinalModifier": null,
+                    "modifiers": [],
+                    "classKeyword": {
+                        "kind": "ClassKeyword",
+                        "textLength": 5
+                    },
+                    "name": {
+                        "kind": "Name",
+                        "textLength": 4
+                    },
+                    "classBaseClause": null,
+                    "classInterfaceClause": null,
+                    "classMembers": {
+                        "ClassMembersNode": {
+                            "openBrace": {
+                                "kind": "OpenBraceToken",
+                                "textLength": 1
+                            },
+                            "classMemberDeclarations": [
+                                {
+                                    "MethodDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "functionKeyword": {
+                                            "kind": "FunctionKeyword",
+                                            "textLength": 8
+                                        },
+                                        "byRefToken": null,
+                                        "name": {
+                                            "kind": "Name",
+                                            "textLength": 11
+                                        },
+                                        "openParen": {
+                                            "kind": "OpenParenToken",
+                                            "textLength": 1
+                                        },
+                                        "parameters": {
+                                            "ParameterDeclarationList": {
+                                                "children": [
+                                                    {
+                                                        "Parameter": {
+                                                            "attributes": null,
+                                                            "visibilityToken": {
+                                                                "kind": "PublicKeyword",
+                                                                "textLength": 6
+                                                            },
+                                                            "modifiers": null,
+                                                            "questionToken": null,
+                                                            "typeDeclarationList": {
+                                                                "QualifiedNameList": {
+                                                                    "children": [
+                                                                        {
+                                                                            "kind": "StringReservedWord",
+                                                                            "textLength": 6
+                                                                        }
+                                                                    ]
+                                                                }
+                                                            },
+                                                            "byRefToken": null,
+                                                            "dotDotDotToken": null,
+                                                            "variableName": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 6
+                                                            },
+                                                            "equalsToken": null,
+                                                            "default": null,
+                                                            "propertyHooks": null
+                                                        }
+                                                    },
+                                                    {
+                                                        "kind": "CommaToken",
+                                                        "textLength": 1
+                                                    },
+                                                    {
+                                                        "Parameter": {
+                                                            "attributes": null,
+                                                            "visibilityToken": {
+                                                                "kind": "PublicKeyword",
+                                                                "textLength": 6
+                                                            },
+                                                            "modifiers": null,
+                                                            "questionToken": null,
+                                                            "typeDeclarationList": {
+                                                                "QualifiedNameList": {
+                                                                    "children": [
+                                                                        {
+                                                                            "kind": "StringReservedWord",
+                                                                            "textLength": 6
+                                                                        }
+                                                                    ]
+                                                                }
+                                                            },
+                                                            "byRefToken": null,
+                                                            "dotDotDotToken": null,
+                                                            "variableName": {
+                                                                "kind": "VariableName",
+                                                                "textLength": 5
+                                                            },
+                                                            "equalsToken": null,
+                                                            "default": null,
+                                                            "propertyHooks": null
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "closeParen": {
+                                            "kind": "CloseParenToken",
+                                            "textLength": 1
+                                        },
+                                        "colonToken": null,
+                                        "questionToken": null,
+                                        "returnTypeList": null,
+                                        "compoundStatementOrSemicolon": {
+                                            "CompoundStatementNode": {
+                                                "openBrace": {
+                                                    "kind": "OpenBraceToken",
+                                                    "textLength": 1
+                                                },
+                                                "statements": [],
+                                                "closeBrace": {
+                                                    "kind": "CloseBraceToken",
+                                                    "textLength": 1
+                                                }
+                                            }
+                                        }
+                                    }
+                                },
+                                {
+                                    "PropertyDeclaration": {
+                                        "attributes": null,
+                                        "modifiers": [
+                                            {
+                                                "kind": "PublicKeyword",
+                                                "textLength": 6
+                                            }
+                                        ],
+                                        "questionToken": null,
+                                        "typeDeclarationList": {
+                                            "QualifiedNameList": {
+                                                "children": [
+                                                    {
+                                                        "kind": "StringReservedWord",
+                                                        "textLength": 6
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyElements": {
+                                            "ExpressionList": {
+                                                "children": [
+                                                    {
+                                                        "AssignmentExpression": {
+                                                            "leftOperand": {
+                                                                "Variable": {
+                                                                    "dollar": null,
+                                                                    "name": {
+                                                                        "kind": "VariableName",
+                                                                        "textLength": 9
+                                                                    }
+                                                                }
+                                                            },
+                                                            "operator": {
+                                                                "kind": "EqualsToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "byRef": null,
+                                                            "rightOperand": {
+                                                                "StringLiteral": {
+                                                                    "startQuote": null,
+                                                                    "children": {
+                                                                        "kind": "StringLiteralToken",
+                                                                        "textLength": 13
+                                                                    },
+                                                                    "endQuote": null
+                                                                }
+                                                            }
+                                                        }
+                                                    }
+                                                ]
+                                            }
+                                        },
+                                        "propertyHooks": {
+                                            "PropertyHooks": {
+                                                "openBrace": {
+                                                    "kind": "OpenBraceToken",
+                                                    "textLength": 1
+                                                },
+                                                "hookDeclarations": [
+                                                    {
+                                                        "PropertyHook": {
+                                                            "attributes": null,
+                                                            "byRefToken": null,
+                                                            "name": {
+                                                                "kind": "Name",
+                                                                "textLength": 3
+                                                            },
+                                                            "openParen": null,
+                                                            "parameters": null,
+                                                            "closeParen": null,
+                                                            "arrowToken": null,
+                                                            "body": {
+                                                                "CompoundStatementNode": {
+                                                                    "openBrace": {
+                                                                        "kind": "OpenBraceToken",
+                                                                        "textLength": 1
+                                                                    },
+                                                                    "statements": [
+                                                                        {
+                                                                            "ReturnStatement": {
+                                                                                "returnKeyword": {
+                                                                                    "kind": "ReturnKeyword",
+                                                                                    "textLength": 6
+                                                                                },
+                                                                                "expression": {
+                                                                                    "StringLiteral": {
+                                                                                        "startQuote": {
+                                                                                            "kind": "DoubleQuoteToken",
+                                                                                            "textLength": 1
+                                                                                        },
+                                                                                        "children": [
+                                                                                            {
+                                                                                                "MemberAccessExpression": {
+                                                                                                    "dereferencableExpression": {
+                                                                                                        "Variable": {
+                                                                                                            "dollar": null,
+                                                                                                            "name": {
+                                                                                                                "kind": "VariableName",
+                                                                                                                "textLength": 5
+                                                                                                            }
+                                                                                                        }
+                                                                                                    },
+                                                                                                    "arrowToken": {
+                                                                                                        "kind": "ArrowToken",
+                                                                                                        "textLength": 2
+                                                                                                    },
+                                                                                                    "memberName": {
+                                                                                                        "kind": "Name",
+                                                                                                        "textLength": 5
+                                                                                                    }
+                                                                                                }
+                                                                                            },
+                                                                                            {
+                                                                                                "kind": "EncapsedAndWhitespace",
+                                                                                                "textLength": 1
+                                                                                            },
+                                                                                            {
+                                                                                                "MemberAccessExpression": {
+                                                                                                    "dereferencableExpression": {
+                                                                                                        "Variable": {
+                                                                                                            "dollar": null,
+                                                                                                            "name": {
+                                                                                                                "kind": "VariableName",
+                                                                                                                "textLength": 5
+                                                                                                            }
+                                                                                                        }
+                                                                                                    },
+                                                                                                    "arrowToken": {
+                                                                                                        "kind": "ArrowToken",
+                                                                                                        "textLength": 2
+                                                                                                    },
+                                                                                                    "memberName": {
+                                                                                                        "kind": "Name",
+                                                                                                        "textLength": 4
+                                                                                                    }
+                                                                                                }
+                                                                                            }
+                                                                                        ],
+                                                                                        "endQuote": {
+                                                                                            "kind": "DoubleQuoteToken",
+                                                                                            "textLength": 1
+                                                                                        }
+                                                                                    }
+                                                                                },
+                                                                                "semicolon": {
+                                                                                    "kind": "SemicolonToken",
+                                                                                    "textLength": 1
+                                                                                }
+                                                                            }
+                                                                        }
+                                                                    ],
+                                                                    "closeBrace": {
+                                                                        "kind": "CloseBraceToken",
+                                                                        "textLength": 1
+                                                                    }
+                                                                }
+                                                            },
+                                                            "semicolon": null
+                                                        }
+                                                    },
+                                                    {
+                                                        "PropertyHook": {
+                                                            "attributes": null,
+                                                            "byRefToken": null,
+                                                            "name": {
+                                                                "kind": "Name",
+                                                                "textLength": 3
+                                                            },
+                                                            "openParen": {
+                                                                "kind": "OpenParenToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "parameters": {
+                                                                "ParameterDeclarationList": {
+                                                                    "children": [
+                                                                        {
+                                                                            "Parameter": {
+                                                                                "attributes": null,
+                                                                                "visibilityToken": null,
+                                                                                "modifiers": null,
+                                                                                "questionToken": null,
+                                                                                "typeDeclarationList": {
+                                                                                    "QualifiedNameList": {
+                                                                                        "children": [
+                                                                                            {
+                                                                                                "kind": "StringReservedWord",
+                                                                                                "textLength": 6
+                                                                                            }
+                                                                                        ]
+                                                                                    }
+                                                                                },
+                                                                                "byRefToken": null,
+                                                                                "dotDotDotToken": null,
+                                                                                "variableName": {
+                                                                                    "kind": "VariableName",
+                                                                                    "textLength": 6
+                                                                                },
+                                                                                "equalsToken": null,
+                                                                                "default": null,
+                                                                                "propertyHooks": null
+                                                                            }
+                                                                        }
+                                                                    ]
+                                                                }
+                                                            },
+                                                            "closeParen": {
+                                                                "kind": "CloseParenToken",
+                                                                "textLength": 1
+                                                            },
+                                                            "arrowToken": null,
+                                                            "body": {
+                                                                "CompoundStatementNode": {
+                                                                    "openBrace": {
+                                                                        "kind": "OpenBraceToken",
+                                                                        "textLength": 1
+                                                                    },
+                                                                    "statements": [
+                                                                        {
+                                                                            "ExpressionStatement": {
+                                                                                "expression": {
+                                                                                    "AssignmentExpression": {
+                                                                                        "leftOperand": {
+                                                                                            "ArrayCreationExpression": {
+                                                                                                "arrayKeyword": null,
+                                                                                                "openParenOrBracket": {
+                                                                                                    "kind": "OpenBracketToken",
+                                                                                                    "textLength": 1
+                                                                                                },
+                                                                                                "arrayElements": {
+                                                                                                    "ArrayElementList": {
+                                                                                                        "children": [
+                                                                                                            {
+                                                                                                                "ArrayElement": {
+                                                                                                                    "elementKey": null,
+                                                                                                                    "arrowToken": null,
+                                                                                                                    "byRef": null,
+                                                                                                                    "dotDotDot": null,
+                                                                                                                    "elementValue": {
+                                                                                                                        "MemberAccessExpression": {
+                                                                                                                            "dereferencableExpression": {
+                                                                                                                                "Variable": {
+                                                                                                                                    "dollar": null,
+                                                                                                                                    "name": {
+                                                                                                                                        "kind": "VariableName",
+                                                                                                                                        "textLength": 5
+                                                                                                                                    }
+                                                                                                                                }
+                                                                                                                            },
+                                                                                                                            "arrowToken": {
+                                                                                                                                "kind": "ArrowToken",
+                                                                                                                                "textLength": 2
+                                                                                                                            },
+                                                                                                                            "memberName": {
+                                                                                                                                "kind": "Name",
+                                                                                                                                "textLength": 5
+                                                                                                                            }
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "kind": "CommaToken",
+                                                                                                                "textLength": 1
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "ArrayElement": {
+                                                                                                                    "elementKey": null,
+                                                                                                                    "arrowToken": null,
+                                                                                                                    "byRef": null,
+                                                                                                                    "dotDotDot": null,
+                                                                                                                    "elementValue": {
+                                                                                                                        "MemberAccessExpression": {
+                                                                                                                            "dereferencableExpression": {
+                                                                                                                                "Variable": {
+                                                                                                                                    "dollar": null,
+                                                                                                                                    "name": {
+                                                                                                                                        "kind": "VariableName",
+                                                                                                                                        "textLength": 5
+                                                                                                                                    }
+                                                                                                                                }
+                                                                                                                            },
+                                                                                                                            "arrowToken": {
+                                                                                                                                "kind": "ArrowToken",
+                                                                                                                                "textLength": 2
+                                                                                                                            },
+                                                                                                                            "memberName": {
+                                                                                                                                "kind": "Name",
+                                                                                                                                "textLength": 4
+                                                                                                                            }
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            }
+                                                                                                        ]
+                                                                                                    }
+                                                                                                },
+                                                                                                "closeParenOrBracket": {
+                                                                                                    "kind": "CloseBracketToken",
+                                                                                                    "textLength": 1
+                                                                                                }
+                                                                                            }
+                                                                                        },
+                                                                                        "operator": {
+                                                                                            "kind": "EqualsToken",
+                                                                                            "textLength": 1
+                                                                                        },
+                                                                                        "byRef": null,
+                                                                                        "rightOperand": {
+                                                                                            "CallExpression": {
+                                                                                                "callableExpression": {
+                                                                                                    "QualifiedName": {
+                                                                                                        "globalSpecifier": null,
+                                                                                                        "relativeSpecifier": null,
+                                                                                                        "nameParts": [
+                                                                                                            {
+                                                                                                                "kind": "Name",
+                                                                                                                "textLength": 7
+                                                                                                            }
+                                                                                                        ]
+                                                                                                    }
+                                                                                                },
+                                                                                                "openParen": {
+                                                                                                    "kind": "OpenParenToken",
+                                                                                                    "textLength": 1
+                                                                                                },
+                                                                                                "argumentExpressionList": {
+                                                                                                    "ArgumentExpressionList": {
+                                                                                                        "children": [
+                                                                                                            {
+                                                                                                                "ArgumentExpression": {
+                                                                                                                    "name": null,
+                                                                                                                    "colonToken": null,
+                                                                                                                    "dotDotDotToken": null,
+                                                                                                                    "expression": {
+                                                                                                                        "StringLiteral": {
+                                                                                                                            "startQuote": null,
+                                                                                                                            "children": {
+                                                                                                                                "kind": "StringLiteralToken",
+                                                                                                                                "textLength": 3
+                                                                                                                            },
+                                                                                                                            "endQuote": null
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "kind": "CommaToken",
+                                                                                                                "textLength": 1
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "ArgumentExpression": {
+                                                                                                                    "name": null,
+                                                                                                                    "colonToken": null,
+                                                                                                                    "dotDotDotToken": null,
+                                                                                                                    "expression": {
+                                                                                                                        "Variable": {
+                                                                                                                            "dollar": null,
+                                                                                                                            "name": {
+                                                                                                                                "kind": "VariableName",
+                                                                                                                                "textLength": 6
+                                                                                                                            }
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "kind": "CommaToken",
+                                                                                                                "textLength": 1
+                                                                                                            },
+                                                                                                            {
+                                                                                                                "ArgumentExpression": {
+                                                                                                                    "name": null,
+                                                                                                                    "colonToken": null,
+                                                                                                                    "dotDotDotToken": null,
+                                                                                                                    "expression": {
+                                                                                                                        "NumericLiteral": {
+                                                                                                                            "children": {
+                                                                                                                                "kind": "IntegerLiteralToken",
+                                                                                                                                "textLength": 1
+                                                                                                                            }
+                                                                                                                        }
+                                                                                                                    }
+                                                                                                                }
+                                                                                                            }
+                                                                                                        ]
+                                                                                                    }
+                                                                                                },
+                                                                                                "closeParen": {
+                                                                                                    "kind": "CloseParenToken",
+                                                                                                    "textLength": 1
+                                                                                                }
+                                                                                            }
+                                                                                        }
+                                                                                    }
+                                                                                },
+                                                                                "semicolon": {
+                                                                                    "kind": "SemicolonToken",
+                                                                                    "textLength": 1
+                                                                                }
+                                                                            }
+                                                                        }
+                                                                    ],
+                                                                    "closeBrace": {
+                                                                        "kind": "CloseBraceToken",
+                                                                        "textLength": 1
+                                                                    }
+                                                                }
+                                                            },
+                                                            "semicolon": null
+                                                        }
+                                                    }
+                                                ],
+                                                "closeBrace": {
+                                                    "kind": "CloseBraceToken",
+                                                    "textLength": 1
+                                                }
+                                            }
+                                        },
+                                        "semicolon": null
+                                    }
+                                }
+                            ],
+                            "closeBrace": {
+                                "kind": "CloseBraceToken",
+                                "textLength": 1
+                            }
+                        }
+                    }
+                }
+            }
+        ],
+        "endOfFileToken": {
+            "kind": "EndOfFileToken",
+            "textLength": 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/cases/parser81/enums6.php.diag b/tests/cases/parser81/enums6.php.diag
index 167edc21..cad4b55f 100644
--- a/tests/cases/parser81/enums6.php.diag
+++ b/tests/cases/parser81/enums6.php.diag
@@ -20,7 +20,7 @@
     {
         "kind": 0,
         "message": "';' expected.",
-        "start": 97,
+        "start": 93,
         "length": 0
     }
 ]
\ No newline at end of file
diff --git a/tests/cases/parser81/enums6.php.tree b/tests/cases/parser81/enums6.php.tree
index 65271765..3ec41620 100644
--- a/tests/cases/parser81/enums6.php.tree
+++ b/tests/cases/parser81/enums6.php.tree
@@ -57,28 +57,15 @@
             {
                 "ExpressionStatement": {
                     "expression": {
-                        "SubscriptExpression": {
-                            "postfixExpression": {
-                                "QualifiedName": {
-                                    "globalSpecifier": null,
-                                    "relativeSpecifier": null,
-                                    "nameParts": [
-                                        {
-                                            "kind": "Name",
-                                            "textLength": 6
-                                        }
-                                    ]
+                        "QualifiedName": {
+                            "globalSpecifier": null,
+                            "relativeSpecifier": null,
+                            "nameParts": [
+                                {
+                                    "kind": "Name",
+                                    "textLength": 6
                                 }
-                            },
-                            "openBracketOrBrace": {
-                                "kind": "OpenBraceToken",
-                                "textLength": 1
-                            },
-                            "accessExpression": null,
-                            "closeBracketOrBrace": {
-                                "kind": "CloseBraceToken",
-                                "textLength": 1
-                            }
+                            ]
                         }
                     },
                     "semicolon": {
@@ -87,6 +74,19 @@
                         "textLength": 0
                     }
                 }
+            },
+            {
+                "CompoundStatementNode": {
+                    "openBrace": {
+                        "kind": "OpenBraceToken",
+                        "textLength": 1
+                    },
+                    "statements": [],
+                    "closeBrace": {
+                        "kind": "CloseBraceToken",
+                        "textLength": 1
+                    }
+                }
             }
         ],
         "endOfFileToken": {
