You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/CHANGELOG.md
+22Lines changed: 22 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,8 +19,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
19
19
-**Import all missing classes.** A bulk code action that imports every unresolved class name in the file at once. Only names with a single unambiguous candidate are imported; ambiguous names are left for manual resolution via the single-class import action. Short-name conflicts are detected and skipped. Appears in the quick-fix menu when the cursor is on an unresolved class name and the file has two or more unresolved names.
20
20
-**Context-aware import candidate filtering.** Import class actions now filter candidates by the syntactic context of the reference. After `implements` only interfaces are offered, after trait `use` only traits, after `extends` only classes or interfaces (as appropriate). This eliminates wrong-kind suggestions and reduces ambiguity for both single and bulk imports.
21
21
22
+
### Changed
23
+
24
+
-**Rewritten variable resolver.** Variable type resolution now uses a single top-to-bottom pass through each function body on both the diagnostic and completion paths, with zero recursion and no depth limit.
25
+
-**Broader type narrowing.**`instanceof`, type-guard functions (`is_array`, `is_string`, etc.), `in_array()` strict mode, `assert()`, `@phpstan-assert-if-true`/`-if-false`, and compound `&&`/`||` conditions now narrow variable types in if/else branches, guard clauses, while-loop bodies, ternary expressions, and `match(true)` arms. Negated guards and compound conditions like `if (!$x instanceof A && !$x instanceof B) { return; }` are handled correctly.
26
+
-**Closure and arrow function parameter inference.** Untyped closure and arrow function parameters are inferred from the enclosing call's callable signature, including through method chains that return `static`. Generic type substitution flows through to inferred parameters (e.g. a `callable(T)` parameter on a `Repository<Product>` resolves `T` to `Product`).
27
+
-**Editing responsiveness.** Classes evicted from the resolved-class cache after a file edit are now eagerly re-populated in dependency order, eliminating lazy resolution delays for diagnostics and completions.
28
+
-**Virtual member resolution.** Virtual member providers (PHPDoc mixins, Laravel model, Eloquent Builder) now resolve completely on every class, eliminating cases where mixins or Eloquent accessors were missing after edits.
29
+
-**Faster analysis.** String interning, O(1) method lookup, and reduced redundant work cut analysis time significantly on large projects.
30
+
22
31
### Fixed
23
32
33
+
-**Infinite loop on array key reassignment patterns.** Files containing `$arr['key'] = f($arr['key'])` or similar read-then-write patterns on the same array key no longer hang the analyzer indefinitely.
34
+
-**`analyze` stack overflow on large codebases.** Worker threads in the `analyze` command no longer overflow the stack on projects with deeply nested PHP files.
35
+
-**Analysis crash on large files.** Files with hundreds of class definitions (e.g. single-file playgrounds) no longer crash with a stack overflow during analysis.
36
+
-**`class-string<T>` parameter completion.** Parameters typed as `class-string<T>` with a bounded template (`@template T of Foo`) now resolve to the bound class during completion and diagnostics. Static member access like `$class::KEY` and `$class::from()` finds members on the bound class.
37
+
-**Inherited parameter types propagate to child methods.** When a child method overrides a parent method without its own `@param` docblock, the parent's richer type annotation (e.g. `list<Pen>` vs bare `array`) is now used for completion inside the child method body.
38
+
-**Docblock `@param` annotations no longer leak across sibling methods.** The docblock scanner now stops at sibling function/method boundaries, preventing `@param` annotations from one method's docblock from being applied to a different method's parameters.
39
+
-**Foreach iterable type alias expansion.** Foreach loops over variables whose type is a `@phpstan-type` alias (e.g. `UserList` defined as `array<int, User>`) now resolve the element type correctly.
40
+
-**Loop-carried assignments visible during completion.** Variables assigned later in a loop body are now discovered during completion inside the same loop.
41
+
-**Variable completion inside assignment RHS sees the pre-assignment type.** When the cursor is inside the right-hand side of a reassignment (e.g. `$request = new Bar(name: $request->)`), the variable retains its previous type for completion.
42
+
-**Scope leakage after closures in chained method calls.** Variables from the enclosing method are no longer invisible after a closure argument in a chained call (e.g. `whereHas(function ($q) { ... })->where('id', $product->id)` now resolves `$product` correctly).
43
+
-**Unresolvable instanceof target no longer causes false diagnostics.** When an instanceof check targets a class that cannot be resolved, the variable's type is cleared rather than keeping the pre-narrowing type, preventing false-positive "method not found" diagnostics.
44
+
-**`@phpstan-assert-if-false self` no longer narrows to the enclosing class.** Guard clauses using cross-file methods with `@phpstan-assert-if-false self` now narrow against the declaring class, not the class that contains the call site.
24
45
-**Short-name collisions eliminated project-wide.** Two unrelated classes sharing a short name (e.g. `App\Carbon` vs `Vendor\Carbon`) were treated as identical across diagnostics, go-to-implementation, trait conflict resolution, throws analysis, and catch-clause filtering. Argument type mismatch diagnostics now use the class hierarchy walker for all Traversable, Stringable, and Countable checks instead of matching short names or suffixes.
25
46
-**Lowercase built-in class names recognized as subtypes of `object`.** PHP classes with lowercase names (e.g. `finfo`, `stdClass` in stubs) are now correctly treated as class-like names in subtype checks, where previously the uppercase-first heuristic rejected them.
26
47
-**Union-typed method calls no longer lose resolution on second occurrence.** When a variable has a union type (e.g. `ClassA|ClassB`) and a method exists only on a later branch, repeated calls in the same file now resolve correctly instead of returning a stale negative cache entry from the first branch.
@@ -36,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
36
57
-**False "undefined variable" diagnostics for by-reference parameters in inherited static methods and constructors.** By-reference parameters in parent class static methods and constructors are now correctly resolved when called on a child class, preventing false diagnostics for variables defined via the call. Class lookup for by-reference resolution now tries the fully-qualified name first, preventing incorrect matches when multiple classes share a short name across namespaces.
37
58
-**Nested `match(true)` expressions.** Multiple `match(true)` expressions sharing narrowed variable names no longer produce incorrect diagnostics.
38
59
-**False-positive type errors on generic class methods resolved through return types.** When a method returns a generic class (e.g. `@return HasMany<Translation, Tag>`), calling a method on the result whose parameter references a class-level template parameter (e.g. `@param TRelatedModel $model`) no longer produces a spurious "expects TRelatedModel, got Translation" diagnostic. The return type's generic arguments are now preserved and used for template substitution. Covers instance methods, static methods, standalone functions, inherited methods, trait methods, and nullable generic return types.
60
+
-**`analyze` and `fix` commands run at consistent speed regardless of invocation style.** Running `cd project && phpantom_lsp analyze` was roughly 8x slower than `phpantom_lsp analyze --project-root project`. Classes parsed during initialization are now found immediately without re-parsing.
39
61
-**Variables prefixed with `$this` receiving stale type information.** Variables like `$thisItem` or `$thisValue` no longer receive stale type information from `$this`-related narrowing.
40
62
-**False-positive type errors on generic class methods and templated static methods.** Class-level `@template` parameters (e.g. `Collection<User>`) are now substituted into method parameter types before checking argument compatibility. Method-level `@template` parameters (e.g. PHPUnit's `assertSame`) are resolved from call-site argument types, covering literals, variables, enum cases, property accesses, and method call return types. Argument type checking correctly rejects `float` passed where an int-range type like `positive-int` is expected.
41
63
-**False "class not found" for global-namespace classes loaded via Composer's `files` autoloading.** Classes like `Mockery` that live in the global namespace and are autoloaded through Composer's `files` directive (rather than PSR-4 or classmap) are now resolved correctly. The class index populated during initialization was never consulted by the class resolver, causing false diagnostics when such classes were imported with `use Mockery;` in namespaced files.
|T25|[Forward-walking scope model](todo/type-inference.md#t25-forward-walking-scope-model-for-variable-type-resolution) (eliminate backward-scanning depth limit) | High | Very High |
29
-
| P9 |[`resolved_class_cache` generic-arg specialisation](todo/performance.md#p9-resolved_class_cache-generic-arg-specialisation)| Medium | Medium |
30
-
| P18 |[Subtype result caching](todo/performance.md#p18-subtype-result-caching) (per-request HashMap for hierarchy walks) | Medium | Low |
31
-
| D4 |[Unused variable diagnostic](todo/diagnostics.md#d4-unused-variable-diagnostic)| Medium | Medium |
32
-
| D12 |[Mago diagnostic proxy](todo/diagnostics.md#d12-mago-diagnostic-proxy)| Medium | Medium |
33
-
| F4 |[Return type and closure parameter type inlay hints](todo/lsp-features.md#f4-return-type-and-closure-parameter-type-inlay-hints)| Medium | Medium |
34
-
| F9 |[Namespace renaming](todo/lsp-features.md#f9-namespace-renaming)| Medium | Medium |
35
-
| A40 |[Convert to instance variable](todo/actions.md#a40-convert-to-instance-variable)| Medium | Medium |
36
-
| D10 |[PHPMD diagnostic proxy](todo/diagnostics.md#d10-phpmd-diagnostic-proxy)| Low | Medium |
| P9 |[`resolved_class_cache` generic-arg specialisation](todo/performance.md#p9-resolved_class_cache-generic-arg-specialisation)| Medium | Medium|
30
+
| P18 |[Subtype result caching](todo/performance.md#p18-subtype-result-caching) (per-request HashMap for hierarchy walks) | Medium | Low|
31
+
| D4 |[Unused variable diagnostic](todo/diagnostics.md#d4-unused-variable-diagnostic)| Medium | Medium|
32
+
| D12 |[Mago diagnostic proxy](todo/diagnostics.md#d12-mago-diagnostic-proxy)| Medium | Medium|
33
+
| F4 |[Return type and closure parameter type inlay hints](todo/lsp-features.md#f4-return-type-and-closure-parameter-type-inlay-hints)| Medium | Medium|
34
+
| F9 |[Namespace renaming](todo/lsp-features.md#f9-namespace-renaming)| Medium | Medium|
35
+
| A40 |[Convert to instance variable](todo/actions.md#a40-convert-to-instance-variable)| Medium | Medium|
| P13 |[Tiered storage: drop per-file maps for non-open files](todo/performance.md#p13-tiered-storage-drop-per-file-maps-for-non-open-files)| Medium-High | Medium-High |
168
167
| P14 |[Eager docblock parsing into structured fields](todo/performance.md#p14-eager-docblock-parsing-into-structured-fields)| Medium | Medium |
169
168
| P10 |[Redundant `parse_and_cache_file` from multiple threads](todo/performance.md#p10-redundant-parse_and_cache_file-from-multiple-threads)| Medium | Low |
170
169
| P11 |[Uncached base-resolution in `build_scope_methods_for_builder`](todo/performance.md#p11-uncached-base-resolution-in-build_scope_methods_for_builder)| Low-Medium | Low |
171
170
| P3 | Parallel pre-filter in `find_implementors`| Low-Medium | Medium |
171
+
| P19 |[Analysis time dominated by per-expression class resolution](todo/performance.md#p19-analysis-time-dominated-by-per-expression-class-resolution) (root cause; fix is ER5) | High | High |
172
172
| P1b | Propagate `Arc<ClassInfo>` through variable-resolution pipeline | Low | Medium |
0 commit comments