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
+3-2Lines changed: 3 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -68,11 +68,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
68
68
-**Diagnostics now work for vendor files open in the editor.** Projects using `--prefer-source` or monorepo setups where libraries are edited from the `vendor/` directory no longer have diagnostics suppressed. Previously all vendor files were unconditionally skipped, hiding syntax errors, deprecation warnings, and all other diagnostics. The `analyze` command also accepts vendor paths as explicit overrides (e.g. `phpantom_lsp analyze vendor/some-package/src/`).
69
69
-**Full-line diagnostic suppression no longer requires a code mapping.** Full-line diagnostics (from tools like PHPStan that only report line numbers) are now suppressed whenever any precise diagnostic exists on the same line, regardless of error codes. Previously suppression relied on a hand-maintained mapping between error codes from different tools, which was incomplete and allowed redundant full-line underlines to obscure the precise location of the issue.
70
70
71
-
-**Scope methods on Eloquent Builder no longer produce false-positive diagnostics.** When a scope method's return type was a bare `Builder` (without generic arguments), subsequent calls in the chain lost the model type and reported scope methods as not found. Bare `Builder` return types on scope methods are now automatically wrapped as `Builder<ConcreteModel>` to preserve the chain. Additionally, unknown method diagnostics on the Eloquent Builder are suppressed when `__call` is present, since Builder dispatches scope methods and Query\Builder calls through it at runtime.
71
+
-**Scope methods on Eloquent Builder no longer produce false-positive diagnostics.** When a scope method's return type was a bare `Builder` (without generic arguments), subsequent calls in the chain lost the model type and reported scope methods as not found. Bare `Builder` return types on scope methods are now automatically wrapped as `Builder<ConcreteModel>` to preserve the chain.
72
72
-**Hover on closure parameters lost inferred generic arguments.** When a closure parameter had an explicit bare class type hint (e.g. `function (Builder $q)`) and the callable signature inferred the same class with generic arguments (e.g. `Builder<Article>`), hover displayed the bare class name instead of the generic version. The inferred type string is now preserved through the resolution pipeline so that hover, completion, and diagnostics all see the same type.
73
73
-**Hover on variables assigned from chained methods lost generic parameters.** When a method returned `static`, `$this`, or `self` and the receiver carried generic parameters (e.g. `Builder<Article>`), hover on a variable assigned from the chain showed the bare class name (`Builder`) instead of the generic version. The receiver's type string is now carried through method call resolution so that self-referencing return types preserve generic parameters across chains.
74
74
-**Hover on the `$` sign of a variable at its assignment site showed no type.** Hovering on the `$` of `$order = new Order()` displayed a bare `$order` with no type information, while hovering one character to the right on `o` worked correctly. The variable's byte offset coincided with the assignment statement's start offset, causing the resolver to skip the assignment entirely. The cursor offset is now nudged past the statement boundary so the assignment is included.
75
-
-**Method chains through `__call` no longer lose the return type.** When a class defines `__call` and an unrecognized method is called (e.g. dynamic `whereColumn()` on an Eloquent Builder), the return type of `__call` is now used as a fallback. If `__call` returns `$this`, `static`, or `self`, the chain type is preserved so subsequent calls continue resolving. Previously the type was lost at the first dynamic call, breaking every link after it.
75
+
-**Method chains through `__call` no longer lose the return type.** When a class defines `__call` and an unrecognized method is called (e.g. dynamic `whereColumn()` on an Eloquent Builder), the return type of `__call` is now used as a fallback. If `__call` returns `$this`, `static`, or `self`, the chain type is preserved so subsequent calls continue resolving. Previously the type was lost at the first dynamic call, breaking every link after it. Eloquent Builder's `__call` is patched from `mixed` to `static` during resolution, matching its runtime behavior (scope dispatch, macro dispatch, and Query\Builder forwarding all return the Builder instance).
76
+
-**Callable parameter inference now preserves generic arguments from the receiver.** When a closure parameter is typed as a bare supertype (e.g. `fn(Builder $q)`) but the enclosing method's callable signature provides a more specific type via `$this` or `static` (e.g. `callable($this)`), the inferred type now carries the receiver's generic arguments. For example, calling `->when($flag, fn(Builder $q) => $q->active())` on a `Builder<Product>` chain now infers `$q` as `Builder<Product>`, so model-specific scope methods resolve correctly instead of being flagged as unknown.
76
77
-**Null narrowing from `!== null` checks in conditions.** When a null-initialized variable was guarded by `$var !== null` in an `if` or `while` condition, the variable still showed `null` in its type inside the condition's `&&` operands and inside the then-body. The `!== null` check (and `!is_null()`, bare truthy guards) now narrows away `null` both for subsequent `&&` operands and inside the corresponding body block. Chained conditions like `$a !== null && $b !== null && $a->method()` narrow all checked variables. Conditions wrapped inside ternary expressions and return statements are also handled.
77
78
-**Variables assigned inside `if`/`while` conditions now resolve in the body.**`if ($admin = AdminUser::first())` and `while ($row = nextRow())` now register the assignment so the variable has a type inside the loop or branch body. Assignments wrapped in comparisons like `if (($conn = getConn()) !== null)` are also recognized.
78
79
-**Fluent chains only flag the first broken link.** In a chain like `$m->callHome()->callMom()->callDad()` where `callHome` does not exist, only `callHome` is flagged. Previously every subsequent link received its own "cannot verify" warning, burying the root cause in noise. Separate statements on the same variable (`$m->callHome(); $m->callMom();`) still flag independently. Scalar member access chains (`$user->getAge()->value->deep`) flag only the first scalar break. Null-safe, static, and mixed-operator chains are all handled.
0 commit comments