Skip to content

Commit 9bddf35

Browse files
committed
Refactor pasers to use AST when possible
1 parent 4b1d6ce commit 9bddf35

29 files changed

Lines changed: 4317 additions & 3870 deletions

docs/ARCHITECTURE.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ src/
1919
├── lib.rs # Backend struct, state, constructors
2020
├── main.rs # Entry point (stdin/stdout LSP transport)
2121
├── server.rs # LSP protocol handlers (initialize, didOpen, completion, …)
22-
├── types.rs # Data structures (ClassInfo, MethodInfo, PropertyInfo, …)
22+
├── types.rs # Data structures (ClassInfo, MethodInfo, SubjectExpr, ResolvedCallableTarget, …)
2323
├── composer.rs # composer.json / PSR-4 autoload parsing
2424
├── stubs.rs # Embedded phpstorm-stubs (build-time generated index)
2525
├── resolution.rs # Multi-phase class/function lookup and name resolution
@@ -48,8 +48,8 @@ src/
4848
│ ├── mod.rs # Submodule declarations
4949
│ ├── handler.rs # Top-level completion request orchestration
5050
│ ├── target.rs # Extract what the user is completing (subject + access kind)
51-
│ ├── resolver.rs # Resolve subject → ClassInfo (type resolution engine)
52-
│ ├── text_resolution.rs # Text-based type resolution (assignment scanning, call chains)
51+
│ ├── resolver.rs # Resolve subject → ClassInfo (type resolution engine), shared resolve_callable_target
52+
│ ├── source_helpers.rs # Source-text scanning helpers (closure/callable return types, new-expression parsing, array access)
5353
│ ├── builder.rs # Build LSP CompletionItems from resolved ClassInfo
5454
│ ├── class_completion.rs # Class name, constant, and function completions
5555
│ ├── variable_completion.rs # Variable name completions and scope collection
@@ -129,21 +129,19 @@ A value of `0` means "not available" (stubs parsed before offsets were stored, s
129129

130130
### Tier 3: Text-Based Fallback (deprecated)
131131

132-
The original line-by-line text scanners (`extract_word_at_position`, `find_member_position_in_range` text search, `line_defines_variable`, `find_definition_position`, `find_function_position`) are retained as fallbacks for:
132+
The remaining line-by-line text scanners (`extract_word_at_position`, `find_member_position_in_range` text search, `find_definition_position`, `find_function_position`) are retained as fallbacks for:
133133

134-
- Stubs and synthetic members where `name_offset == 0`
134+
- Stubs and synthetic members where `name_offset == 0` or `keyword_offset == 0`
135135
- Files where the parser panicked (malformed PHP) and no symbol map exists
136-
- The go-to-implementation subsystem (not yet migrated to the symbol map)
137136

138-
These functions are marked `#[deprecated]` with phase notes. They will be removed once the AST-based paths have been stable for a release cycle.
137+
These functions are marked `#[deprecated]` with phase notes. They will be removed once stubs and synthetic members store valid byte offsets during parsing.
139138

140139
### Variable Definition Resolution
141140

142-
Variable go-to-definition (`$var` → jump to definition) uses three layers:
141+
Variable go-to-definition (`$var` → jump to definition) uses two layers:
143142

144143
1. **Symbol map** (`var_defs`): the primary path. Finds the most recent `VarDefSite` before the cursor within the enclosing scope. When the cursor is physically on a definition token (parameter, foreach binding, catch variable), it returns `None` so the caller can fall through to type-hint resolution.
145-
2. **AST-based search** (`resolve_variable_definition_ast`): parses the file and walks the enclosing scope to find the definition site. Handles destructuring (`[$a, $b] = ...`, `list($a, $b) = ...`) and nested scopes correctly. Used as a fallback when the symbol map doesn't have a match.
146-
3. **Text-based search** (`resolve_variable_definition_text`): the original heuristic line scanner. Only activated when the AST parse fails.
144+
2. **AST-based search** (`resolve_variable_definition_ast`): parses the file and walks the enclosing scope to find the definition site. Handles destructuring (`[$a, $b] = ...`, `list($a, $b) = ...`) and nested scopes correctly. Used as a fallback when the symbol map doesn't have a match. Returns `None` when the AST parse fails.
147145

148146
## Signature Help Architecture
149147

docs/CHANGELOG.md

Lines changed: 46 additions & 139 deletions
Large diffs are not rendered by default.

docs/todo.md

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -824,39 +824,7 @@ See `ClosureBindDynamicReturnTypeExtension` and
824824

825825
---
826826

827-
### 27. Remove deprecated text-search fallbacks
828-
**Impact: Low · Effort: Medium**
829-
830-
The go-to-definition subsystem now uses the precomputed `SymbolMap` as
831-
its primary path and stored byte offsets (`name_offset`, `keyword_offset`)
832-
for cross-file jumps. The original line-by-line text scanners are marked
833-
`#[deprecated]` and retained only as fallbacks for:
834-
835-
- Stubs and synthetic members where `name_offset == 0`
836-
- Files where the parser panicked and no symbol map exists
837-
- The go-to-implementation subsystem (not yet migrated)
838-
839-
Once the AST-based paths have been stable for a release cycle, these
840-
deprecated functions can be removed:
841-
842-
| Function | File | Replacement |
843-
|---|---|---|
844-
| `find_definition_position` | `definition/resolve.rs` | `ClassInfo::keyword_offset` + `offset_to_position` |
845-
| `find_function_position` | `definition/resolve.rs` | `FunctionInfo::name_offset` + `offset_to_position` |
846-
| `find_define_position` | `definition/resolve.rs` | Store `define()` offset during parsing |
847-
| `extract_word_at_position` | `definition/resolve.rs` | `SymbolMap::lookup` |
848-
| `resolve_variable_definition_text` | `definition/variable.rs` | `SymbolMap::find_var_definition` + AST walk |
849-
| `line_defines_variable` | `definition/variable.rs` | (only used by `resolve_variable_definition_text`) |
850-
| `find_member_position_in_range` text path | `definition/member.rs` | `name_offset` + `offset_to_position` |
851-
852-
The go-to-implementation subsystem (`resolve_implementation` in
853-
`definition/implementation.rs`) still uses `extract_word_at_position`
854-
for cursor context detection. Migrating it to use `SymbolMap::lookup`
855-
would let that deprecated function be removed entirely.
856-
857-
---
858-
859-
### 28. Non-array functions with dynamic return types
827+
### 27. Non-array functions with dynamic return types
860828
**Impact: Low · Effort: High**
861829

862830
PHPStan also provides dynamic return type extensions for many non-array
@@ -887,7 +855,7 @@ return types (less impactful for class-based completion).
887855

888856
---
889857

890-
### 29. Language construct signature help and hover
858+
### 28. Language construct signature help and hover
891859
**Impact: Low · Effort: Low**
892860

893861
PHP language constructs that use parentheses (`unset()`, `isset()`, `empty()`,
@@ -904,22 +872,22 @@ need a similar hardcoded lookup.
904872

905873
---
906874

907-
### 30. Diagnostics
875+
### 29. Diagnostics
908876
**Impact: Low (large scope) · Effort: Very High**
909877

910878
No error reporting (undefined methods, type mismatches, etc.).
911879

912880
---
913881

914-
### 31. Code Actions
882+
### 30. Code Actions
915883
**Impact: Low · Effort: Very High**
916884

917885
No quick fixes or refactoring suggestions. No `codeActionProvider` in
918886
`ServerCapabilities`, no `textDocument/codeAction` handler, and no
919887
`WorkspaceEdit` generation infrastructure beyond trivial `TextEdit`s for
920888
use-statement insertion.
921889

922-
#### 31a. Extract Function refactoring
890+
#### 30a. Extract Function refactoring
923891

924892
Select a range of statements inside a method/function and extract them into a
925893
new function. The LSP would need to:

0 commit comments

Comments
 (0)