Skip to content

Commit 94ad9f0

Browse files
committed
Rename ast_map and classmap to uri_classes_index and fqn_uri_index
1 parent 211c97b commit 94ad9f0

51 files changed

Lines changed: 769 additions & 578 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/ARCHITECTURE.md

Lines changed: 86 additions & 60 deletions
Large diffs are not rendered by default.

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5858

5959
### Fixed
6060

61+
- **Go-to-definition.** Fixed a potential deadlock when navigating to a vendor class that hadn't been parsed yet.
6162
- **LSP no longer freezes under heavy editor activity.** Server-to-client requests (diagnostic refresh, progress token creation) could deadlock the service loop when the editor was simultaneously sending bursts of open/close/hover messages. All server-to-client requests are now either fire-and-forget or time-bounded, long-running handlers are cancellation-safe, and the process exits cleanly if the service loop ever terminates unexpectedly.
6263
- **Rename class preserves `self`, `static`, and `parent` keywords.** Renaming a class no longer replaces occurrences of `self::`, `static::`, or `parent::` with the new class name.
6364
- **Rename propagates into closures and arrow functions.** Renaming a variable now follows explicit `use ($var)` captures into closure bodies and implicit captures into arrow function bodies, instead of leaving those occurrences unchanged.

docs/todo/blade.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ On `<x-alert>`:
322322

323323
1. Extract the component name.
324324
2. Look up in `blade_components` to get the FQN.
325-
3. Use `find_or_load_class` + `class_index` / `classmap` to find the
325+
3. Use `find_or_load_class` + `fqn_uri_index` to find the
326326
source file.
327327
4. Return a `Location` pointing to the class definition.
328328

docs/todo/completion.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ and no go-to-definition because PHPantom has no record of the alias.
257257

258258
3. **Cross-file aliases** — for aliases defined in autoloaded files
259259
(e.g. a `_ide_helper.php` or a framework bootstrap file), the alias
260-
mapping needs to be stored in `class_index` or a parallel index so
260+
mapping needs to be stored in `fqn_uri_index` or a parallel index so
261261
that it's available project-wide. This is the main effort: deciding
262262
where to persist the alias data and when to scan for it.
263263

@@ -343,14 +343,14 @@ improves perceived latency on keystroke.
343343
**Impact: Low · Effort: Low**
344344

345345
Same-namespace classes (source tier 2) already carry deprecation info
346-
because `ClassInfo` is available. Classes from `class_index`, classmap,
347-
and stubs (tiers 3-5) don't check for `@deprecated` because the class
346+
because `ClassInfo` is available. Classes from `fqn_uri_index`
347+
and stubs (tiers 3-4) don't check for `@deprecated` because the class
348348
may not be fully loaded at completion time.
349349

350350
For classmap entries, a lightweight byte-level scan of the first
351351
docblock in the file (similar to `detect_stub_class_kind`) could detect
352352
`@deprecated` without a full parse. For stubs, the source is already
353-
in memory and could be scanned cheaply. For class_index entries, the
353+
in memory and could be scanned cheaply. For fqn_uri_index entries, the
354354
deprecation flag could be stored alongside the file path when the class
355355
is first indexed.
356356

docs/todo/eager-resolution.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ dominates analysis time.
8585
- **Phase 1: MethodStore infrastructure.** Added
8686
`Backend.method_store: MethodStore` (an
8787
`Arc<RwLock<HashMap<(String, String), Arc<MethodInfo>>>>`)
88-
populated alongside `fqn_index` in `update_ast_inner` and
88+
populated alongside `fqn_class_index` in `update_ast_inner` and
8989
`parse_and_cache_content_versioned`. Eviction on re-parse
9090
via `evict_methods_for_fqns`.
9191
- **Phase 2: Centralised lookup helpers.** Added
@@ -349,7 +349,7 @@ The problem is needing them for every runtime analysis thread.
349349
speed (sub-second).
350350
- No test regressions in the existing test suite.
351351
- Memory usage for the populated metadata is within 2x of the
352-
current `ast_map` + `class_index` combined size.
352+
current `uri_classes_index` + `fqn_uri_index` combined size.
353353
- The diagnostic/analysis pass never calls
354354
`resolve_class_with_inheritance`. All class metadata is
355355
pre-populated and immutable during analysis.

docs/todo/external-stubs.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ PHPantom embeds JetBrains phpstorm-stubs at compile time via
2222
`build.rs`. The stubs are baked into the binary as static string
2323
arrays and indexed by class, function, and constant name. At runtime,
2424
`find_or_load_class` checks the `stub_index` as a final fallback
25-
(Phase 3) after `ast_map`, classmap, and PSR-4. Stub files are parsed
25+
(Phase 3) after `uri_classes_index`, `fqn_uri_index`, and PSR-4. Stub files are parsed
2626
lazily on first access and cached under `phpantom-stub://` URIs.
2727

2828
This works well for the PHP standard library but has limitations:
@@ -321,13 +321,13 @@ embedded stubs:
321321

322322
**`find_or_load_class`:**
323323

324-
1. Phase 1: `ast_map` (user code, already-parsed files)
325-
2. Phase 1.5: Composer classmap
324+
1. Phase 0: `fqn_class_index` (user code, already-parsed files)
325+
2. Phase 1: fqn_uri_index
326326
3. Phase 2: PSR-4
327327
4. **Phase 2.5 (new): External stub class index.** Checks the
328328
unified external stub index (populated from `.phpantom.toml`,
329329
Composer stubs, and IDE-provided stubs in priority order). Read
330-
the file, parse and cache in `ast_map` under a
330+
the file, parse and cache in `uri_classes_index` under a
331331
`phpantom-ext-stub://` URI.
332332
5. Phase 3: Embedded stubs
333333

docs/todo/indexing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ the last report, avoiding notification spam.
409409
### Go to Implementation
410410

411411
`find_implementors` has five phases with different totals:
412-
Phase 1 (ast_map), Phase 2 (class_index), Phase 3 (classmap
412+
Phase 0 (fqn_class_index), Phase 1 (fqn_uri_index), Phase 2 (PSR-4
413413
files), Phase 4 (stubs), Phase 5 (PSR-4 walk). The total for
414414
each phase is known before iteration begins. Report progress
415415
within each phase and allocate percentage ranges across phases

docs/todo/refactor.md

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,98 @@ Each item must include:
214214

215215
## Outstanding items
216216

217-
No outstanding items.
217+
### R1. Shadow chain resolver in `helpers.rs` 🔴
218+
219+
`src/completion/source/helpers.rs` L625–780 contains `resolve_lhs_to_class` /
220+
`resolve_raw_type_from_call_chain`, a full parallel chain resolver that manually
221+
splits on `->` and `::` via `rfind`, handles `$this->prop`, `new Foo()`,
222+
`ClassName::method()`, and recursive call chains by string manipulation. This
223+
duplicates what `SubjectExpr::parse` + `resolve_target_classes_expr` in
224+
`completion/resolver.rs` already does. Also called from
225+
`extract_first_class_callable_return_type` (L407–470, same file).
226+
227+
**Action:** Replace with calls through the main `SubjectExpr::parse` +
228+
`resolve_target_classes_expr` pipeline.
229+
230+
**Files:** `src/completion/source/helpers.rs`
231+
232+
### R2. Mini expression resolver in `call_resolution.rs` 🔴
233+
234+
`src/completion/call_resolution.rs` L2528 has `resolve_expression_to_type`, a
235+
separate expression-to-`PhpType` resolver for conditional return type argument
236+
matching. Handles `SubjectExpr::PropertyChain` and `SubjectExpr::Variable`
237+
through different code paths than the forward walker's `resolve_rhs_expression`.
238+
Fewer cases, different path, will diverge as features are added.
239+
240+
**Action:** Route through the shared `resolve_rhs_expression` /
241+
`resolve_expression_type` pipeline.
242+
243+
**Files:** `src/completion/call_resolution.rs`
244+
245+
### R3. Backward `@var` scanner in `docblock/tags.rs` 🟠
246+
247+
`src/docblock/tags.rs` L520–620 (`find_var_raw_type_in_source`) and L1000–1100
248+
(`find_iterable_raw_type_in_source`) scan source lines backward with
249+
`lines().rev()`, manually tracking brace depth and sibling scopes via character
250+
counting. This reimplements scope analysis that the forward walker and
251+
`scope_collector/` already provide. Called from `call_resolution.rs` (L1251,
252+
L2793) and `array_shape.rs` (L519), making them load-bearing.
253+
254+
**Action:** Replace with forward walker variable resolution or ensure the
255+
forward walker captures `@var` annotations so these backward scanners become
256+
unnecessary.
257+
258+
**Files:** `src/docblock/tags.rs`, `src/completion/call_resolution.rs`,
259+
`src/completion/array_shape.rs`
260+
261+
### R4. Backward callable scan in `signature_help.rs` 🟠
262+
263+
`src/signature_help.rs` L438–470 (`extract_callable_target_from_variable`) uses
264+
raw `rfind` backward text scan to find `$fn = someTarget(...)` assignments,
265+
bypassing the forward walker which already tracks variable assignments.
266+
267+
**Action:** Use the forward walker's assignment tracking instead of raw text
268+
scanning.
269+
270+
**Files:** `src/signature_help.rs`
271+
272+
### R5. Hover double-resolution 🟠
273+
274+
`src/hover/mod.rs` L984–1030: `hover_variable` calls `resolve_variable_php_type`
275+
(which internally runs `resolve_variable_types` via the forward walker). If that
276+
returns `None`, it calls `resolve_variable_types` again directly. Since the first
277+
function already called the second internally, the second call is dead code.
278+
279+
**Action:** Remove the redundant second call to `resolve_variable_types`.
280+
281+
**Files:** `src/hover/mod.rs`
282+
283+
---
284+
285+
## Intentional overlap (reference, not actionable)
286+
287+
These are parallel systems that exist for valid reasons. Documented here so
288+
they are factored into future design decisions.
289+
290+
### `uri_classes_index` vs `fqn_class_index` vs `fqn_uri_index`
291+
292+
Three maps storing overlapping class data:
293+
- `uri_classes_index`: URI → `Vec<Arc<ClassInfo>>` (needed for "all classes in this file")
294+
- `fqn_class_index`: FQN → `Arc<ClassInfo>` (O(1) FQN lookup)
295+
- `fqn_uri_index`: FQN → URI string (survives `didClose`, enables re-loading)
296+
297+
Data is shared via `Arc`. Each serves a distinct query pattern.
298+
299+
### PathBuf ↔ URI round-tripping
300+
301+
The classmap scanner produces `PathBuf`, converts to URI strings for
302+
`fqn_uri_index`, then at lookup time parses back to `PathBuf` for file reading.
303+
A minor inefficiency, not a parallel system.
304+
305+
### `find_class_in_ast_map` linear scan fallback
306+
307+
`src/util.rs` L1436–1447: After the O(1) `fqn_class_index` lookup fails, falls
308+
back to linear scan of `uri_classes_index`. Covers race conditions during initial
309+
indexing. Legitimate safety net.
218310

219311

src/analyse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ pub async fn run(options: AnalyseOptions) -> i32 {
251251
// read lock, then drop the lock before resolving. Resolution may
252252
// call find_or_load_class which takes write locks on ast_map.
253253
let sorted_fqns = {
254-
let ast_map = backend.ast_map.read();
254+
let ast_map = backend.uri_classes_index.read();
255255
crate::toposort::toposort_from_ast_map(&ast_map)
256256
};
257257
// Run eager population on a large-stack thread. `resolve_class_fully_inner`

src/code_actions/change_visibility.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,12 @@ impl Backend {
259259
member_kind: &MemberKind,
260260
) -> Option<u8> {
261261
// Find the enclosing ClassInfo from the ast_map.
262-
let local_classes: Vec<Arc<ClassInfo>> =
263-
self.ast_map.read().get(uri).cloned().unwrap_or_default();
262+
let local_classes: Vec<Arc<ClassInfo>> = self
263+
.uri_classes_index
264+
.read()
265+
.get(uri)
266+
.cloned()
267+
.unwrap_or_default();
264268

265269
let enclosing = crate::util::find_class_at_offset(&local_classes, cursor_offset)?;
266270

0 commit comments

Comments
 (0)