Skip to content

Commit 842702a

Browse files
authored
Merge pull request #1 from phan/update-tolerant
Update tolerant parser for PHP 8.3+ syntax (hooks, pipe operator)
2 parents 457738c + 3e90f48 commit 842702a

34 files changed

+798
-183
lines changed

TOLERANT_TODO.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Tolerant Parser Update TODO
2+
3+
## Overview
4+
5+
This branch tracks work needed to bring the tolerant PHP parser up to date for PHP 8.3, 8.4, and 8.5 so that downstream consumers (notably Phan) can rely on it when the native `ext-ast` extension is unavailable. The fork currently matches upstream commit `457738cbe` (September 2024) and is missing several recent language features.
6+
7+
## Current Status
8+
9+
- PHPUnit suites `invariants` and `api` pass locally when run with `zend.assertions=1` and `assert.exception=1`.
10+
- Grammar fixtures (`tests/cases/parser*.tree/.diag`) were generated before the latest language changes; they will need regeneration once parser updates land.
11+
- Validation tests that depend on large submodules (`validation/frameworks/*`) remain skipped/not vendored.
12+
13+
## Gaps / Work Items
14+
15+
### PHP 8.3
16+
17+
Double-check tolerant against the language changes that shipped with 8.3:
18+
19+
- **Dynamic class constant fetch** (`Foo::{expr}`): ensure tokenizer/grammar accept brace-wrapped expressions after `::`, add fixtures, and mirror php-ast node structure. **(verified via `tests/samples/dynamic_class_const.php`)**
20+
- **Typed class constants / readonly amendments**: confirm class-constant declarations propagate their type information and `readonly` constraints into tolerant AST output.
21+
- **`#[\Override]` attribute**: attributes already parse, but we should add fixtures to verify tolerant preserves them on methods.
22+
- **Arbitrary static variable initialisers**: while this is largely semantic, tolerant should accept the new grammar in function-level `static` declarations and update diagnostics if necessary.
23+
24+
### PHP 8.4
25+
26+
- **Property access hooks** (`public int $count { get => ...; set { ... } }`): **(implemented)**
27+
- Tokenizer must recognise `get`/`set` (and hook modifiers) in this context.
28+
- Introduce AST nodes for hook lists/bodies that align with php-ast’s `AST_PROP_ELEM` `hooks` child.
29+
- Update diagnostics to catch invalid hook combinations.
30+
- Conversion now produces `AST_PROPERTY_HOOK`/`AST_PROPERTY_HOOK_SHORT_BODY` nodes; add regression coverage to guard against regressions.
31+
- **Asymmetric visibility v2** (`public(set) private(get) $prop;`): extend the modifier grammar, update `TokenKind`, and cover tolerant AST flag handling.
32+
- **`new Foo()->bar()` without wrapping parentheses**: confirm parser handles the reduced precedence and add regression tests.
33+
- **Property hook improvements** (hook attributes, multiple hooks per property, etc.): ensure attribute placement and hook ordering are represented correctly.
34+
- Audit additional 8.4 deprecations that change parsing (e.g. implicit nullable parameters) to ensure tolerant still emits matching diagnostics.
35+
36+
### PHP 8.5 (in progress)
37+
38+
Monitor RFCs merged into php-src and mirror the token/grammar changes, for example:
39+
40+
- **Pipe operator** (`expr |> func(...)`): add new tokens, precedence rules, AST nodes, and fixtures.
41+
- **`clone with`** expressions: model the new syntax (`clone $obj with { prop: value }`) and ensure node mapping covers the initializer list.
42+
- **Final property promotion** (`final public function __construct(private final string $x) {}`): allow `final` in promoted parameters and carry flags into tolerant AST.
43+
- **Attributes on constants / extended attribute targets**: verify attributes on constants and traits are preserved.
44+
- **Extend `#[\Override]` to properties / `#[\NoDiscard]` / `#[\DelayedTargetValidation]`**: attributes already parse, but add regression coverage to ensure tolerant does not misclassify their targets.
45+
- Track any additional keywords (`with`, operator tokens, etc.) and update `TokenKind.php` / `TokenStringMaps.php` accordingly.
46+
47+
### Diagnostics & Node Mapping
48+
49+
- Review `TokenKind`/`TokenStringMaps` for completeness after adding new tokens.
50+
- Ensure node classes record line/column spans needed by language-server features when hooks and new constructs are present.
51+
- Verify edits still map correctly (`TextEdit`, node mapping) for newly supported syntax.
52+
53+
### Tests & Tooling
54+
55+
- Add focused fixtures for property hooks and any new constructs under `tests/cases/parser` (and regenerate `.tree/.diag`).
56+
- Re-enable / update tolerant PHPUnit suites once fixtures are refreshed.
57+
- Consider syncing targeted tests from Phan’s fallback suite to catch divergences early.
58+
59+
### Integration with Phan
60+
61+
- After implementing features, run Phan’s fallback parser tests (`./tests/run_test __FakeSelfFallbackTest`) to ensure parity.
62+
- Publish updated tags / commit references so Phan can subtree merge the changes into `third_party/phan-tolerant`.
63+
64+
### Verification Strategy
65+
66+
- **AST comparison**: use Phan’s `tools/dump_ast.php` (php-ast) to capture the expected AST for new syntax. For the tolerant side, run `php tools/PrintTolerantAst.php` (raw tolerant tree) in combination with Phan’s `internal/dump_fallback_ast.php` (uses the converter) to ensure both the parse tree and converted php-ast structures match expectations.
67+
- **PHP runtime selection**: on this dev machine we can run `sudo newphp 83`, `sudo newphp 84`, etc. to switch CLI versions; other environments may require Docker images, phpenv, asdf, etc. Record the PHP version used when regenerating fixtures.
68+
- **Leverage Phan fixtures**: pull feature-specific testcases (e.g. property hooks, asymmetric visibility) from `phan/tests/files/src` into tolerant’s parser tests to validate new constructs.
69+
- **Run tolerant PHPUnit suites**: keep `vendor/bin/phpunit --testsuite invariants,api` (with `zend.assertions=1`) as a fast regression check while iterating.
70+
71+
Recommended sample inputs for AST diffs (update as new fixtures are added):
72+
73+
| Feature | Sample file(s) | Min PHP | Native AST dump | Tolerant dump |
74+
| --- | --- | --- | --- | --- |
75+
| Dynamic class const fetch | `tests/samples/dynamic_class_const.php` | 8.3 | `php ~/phan/tools/dump_ast.php --json …` | `php tools/PrintTolerantAst.php …` + `php ~/phan/internal/dump_fallback_ast.php --php-ast …` |
76+
| Property hooks | `tests/samples/property_hooks.php` | 8.4 | (run after `sudo newphp 84`) | same as above |
77+
| Asymmetric visibility props | (add fixture) | 8.4 |||
78+
| Pipe operator | `tests/samples/pipe_operator.php` | 8.5 | `php ~/phan/tools/dump_ast.php --json …` | `php tools/PrintTolerantAst.php …`, `php ~/phan/internal/dump_fallback_ast.php --php-ast …` |
79+
| `clone with` expressions | (add fixture) | 8.5 |||
80+
81+
## Next Steps
82+
83+
1. Audit existing fixtures vs php-src 8.3/8.4 syntax to catalogue precise failures.
84+
2. Prototype grammar/tokenizer changes for property hooks and regenerate associated AST nodes.
85+
3. Update diagnostics and tolerant AST converter expectations in tandem with Phan.
86+
4. Refresh fixtures and CI to cover the new syntax.
87+
5. Coordinate subtree sync back into Phan once tests pass.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
/*---------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All rights reserved.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
namespace Microsoft\PhpParser\Node\DelimitedList;
8+
9+
use Microsoft\PhpParser\Node\DelimitedList;
10+
11+
class PropertyElementList extends DelimitedList {
12+
}

src/Node/PropertyDeclaration.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Microsoft\PhpParser\ModifiedTypeInterface;
1111
use Microsoft\PhpParser\ModifiedTypeTrait;
1212
use Microsoft\PhpParser\Node;
13+
use Microsoft\PhpParser\Node\DelimitedList\PropertyElementList;
1314
use Microsoft\PhpParser\Node\DelimitedList\QualifiedNameList;
1415
use Microsoft\PhpParser\Token;
1516

@@ -25,7 +26,7 @@ class PropertyDeclaration extends Node implements ModifiedTypeInterface {
2526
/** @var QualifiedNameList|MissingToken|null */
2627
public $typeDeclarationList;
2728

28-
/** @var DelimitedList\ExpressionList */
29+
/** @var PropertyElementList */
2930
public $propertyElements;
3031

3132
/** @var Token */

src/Node/PropertyElement.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
/*---------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All rights reserved.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
namespace Microsoft\PhpParser\Node;
8+
9+
use Microsoft\PhpParser\Node;
10+
use Microsoft\PhpParser\Node\Expression\Variable;
11+
use Microsoft\PhpParser\Token;
12+
13+
class PropertyElement extends Node {
14+
/** @var Variable|null */
15+
public $variable;
16+
17+
/** @var Token|null */
18+
public $equalsToken;
19+
20+
/** @var Node|null */
21+
public $initializer;
22+
23+
/** @var PropertyHookList|null */
24+
public $hookList;
25+
26+
const CHILD_NAMES = [
27+
'variable',
28+
'equalsToken',
29+
'initializer',
30+
'hookList',
31+
];
32+
}

src/Node/PropertyHook.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/*---------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All rights reserved.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
namespace Microsoft\PhpParser\Node;
8+
9+
use Microsoft\PhpParser\Node;
10+
use Microsoft\PhpParser\Node\DelimitedList\ParameterDeclarationList;
11+
use Microsoft\PhpParser\Node\Statement\CompoundStatementNode;
12+
use Microsoft\PhpParser\Token;
13+
14+
class PropertyHook extends Node {
15+
/** @var AttributeGroup[]|null */
16+
public $attributes;
17+
18+
/** @var Token|null */
19+
public $hookKeyword;
20+
21+
/** @var Token|null */
22+
public $openParen;
23+
24+
/** @var ParameterDeclarationList|null */
25+
public $parameterList;
26+
27+
/** @var Token|null */
28+
public $closeParen;
29+
30+
/** @var Token|null */
31+
public $arrowToken;
32+
33+
/** @var Node|null */
34+
public $expression;
35+
36+
/** @var CompoundStatementNode|null */
37+
public $compoundStatement;
38+
39+
/** @var Token|null */
40+
public $semicolon;
41+
42+
const CHILD_NAMES = [
43+
'attributes',
44+
'hookKeyword',
45+
'openParen',
46+
'parameterList',
47+
'closeParen',
48+
'arrowToken',
49+
'expression',
50+
'compoundStatement',
51+
'semicolon',
52+
];
53+
}

src/Node/PropertyHookList.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/*---------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All rights reserved.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
namespace Microsoft\PhpParser\Node;
8+
9+
use Microsoft\PhpParser\Node;
10+
use Microsoft\PhpParser\Token;
11+
12+
class PropertyHookList extends Node {
13+
/** @var Token|null */
14+
public $openBrace;
15+
16+
/** @var PropertyHook[]|null */
17+
public $hooks;
18+
19+
/** @var Token|null */
20+
public $closeBrace;
21+
22+
const CHILD_NAMES = [
23+
'openBrace',
24+
'hooks',
25+
'closeBrace',
26+
];
27+
}

0 commit comments

Comments
 (0)