Skip to content

Commit ae8c211

Browse files
committed
Faster assert()/type-guard narrowing during the forward walk
1 parent 8c704fd commit ae8c211

3 files changed

Lines changed: 1 addition & 21 deletions

File tree

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5555
- **The `analyze` and `fix` CLI subcommands no longer build the cross-file reference index.** That index only serves Find References, Rename, and reference-count inlay hints, none of which the CLI subcommands query, so skipping it removes wasted work from whole-project `analyze` and `fix` runs. The editor's LSP session is unaffected.
5656
- **Faster project startup.** The parts of startup that still ran on a single core now use all of them. Every autoload directory in the project, your own and each vendor package's, is walked in one pass that shares work between cores at the directory level, so a single very large dependency no longer holds up the rest of the scan, and the ignore rules above those directories are compiled once for the whole project instead of once per package. A bundled tool archive such as PHPStan's `.phar` is now read through a memory map with only its file index retained, rather than copied into memory whole. On a large Laravel project this cuts the indexing phase by roughly a quarter and lowers peak memory by around 25 MB. Discovered files are now sorted, so when two files declare the same class name the one that wins is the same on every run instead of depending on the order the filesystem happened to return.
5757
- **Class origin classification no longer re-scans the whole classmap after the fact.** Startup used to look up each discovered class's completion-ranking origin (project, explicit dependency, transitive dependency, core stub) by re-reading and re-parsing `installed.json` a second time and prefix-matching every class's file path against the package list on a single thread. The origin is now attached to a class the moment it is discovered during the already-parallel vendor scan, the same way it already worked for functions and constants, removing both the duplicate parse and the serial pass.
58+
- **Faster `assert()`/type-guard narrowing during the forward walk.** Every statement used to build a fresh resolution context (including a scope clone) for each in-scope variable to check whether it was an `assert()` or `@phpstan-assert`/`@psalm-assert` call, even for statements that could never be one. Non-call statements now skip that work entirely, cutting a measurable share of the walk on methods with many locals and many statements.
5859

5960
### Removed
6061

docs/todo.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,6 @@ unlikely to move the needle for most users.
211211
| P20 | [Content-hash gated resolution cache persistence](todo/performance.md#p20-content-hash-gated-resolution-cache-persistence) | Medium | Medium |
212212
| P21 | [Offset-shifting for cached diagnostics on partial edits](todo/performance.md#p21-offset-shifting-for-cached-diagnostics-on-partial-edits) | Medium | Medium |
213213
| P23 | [`workspace/symbol` lowercases every symbol name per request](todo/performance.md#p23-workspacesymbol-allocates-a-lowercase-copy-of-every-symbol-name-per-request) | Low-Medium | Low |
214-
| P28 | [`process_assert_narrowing` clones the scope per variable for every statement](todo/performance.md#p28-process_assert_narrowing-clones-the-top-level-scope-once-per-variable-for-every-statement) | Low-Medium | Low |
215214
| | **[Indexing](todo/indexing.md)** | | |
216215
| X3 | Completion item detail on demand (`completionItem/resolve`) | Medium | Medium |
217216
| X7 | [Recency tracking](todo/indexing.md#x7-recency-tracking) | Medium | Medium |

docs/todo/performance.md

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -658,26 +658,6 @@ reuse the resolution already performed.
658658

659659
---
660660

661-
## P28. `process_assert_narrowing` clones the top-level scope once per variable for every statement
662-
663-
**Impact: Low-Medium · Effort: Low**
664-
665-
The forward walker's assert-narrowing step runs for every statement
666-
and clones `ctx.top_level_scope` once per variable currently in
667-
scope, even when the statement is not a call expression and can
668-
therefore never be an `assert()` / custom type-guard call. On
669-
methods with many locals and many statements the clones add up to a
670-
measurable share of the walk.
671-
672-
Add an early-out that skips the per-variable loop when the statement
673-
cannot be an assertion (not a call expression). This was confirmed
674-
as an independently worthwhile micro-fix while investigating the
675-
PclZip return-type-inference hang, but was reverted at the time
676-
because it did not address that hang's dominant cost (uncached
677-
body-return inference, since fixed by memoization).
678-
679-
---
680-
681661
## P46. `mago-phpdoc-syntax` cannot parse `@method static (…) name()`
682662

683663
**Impact: Low · Effort: Low (upstream)**

0 commit comments

Comments
 (0)