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
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -75,6 +75,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
75
75
-**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.
76
76
-**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).
77
77
-**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.
78
+
-**`stdClass` and `object` types no longer produce false-positive diagnostics.** Variables typed as `object` (via parameter type, `@var`, or type-guard narrowing) and `stdClass` now permit arbitrary property access without `unresolved_member_access` diagnostics. Type-guard functions like `is_object()` now narrow `mixed` to `object` instead of filtering it to empty. Compound `&&` conditions propagate type-guard narrowing to both the condition's right-hand operands and the if-body, so `if (is_object($data) && $data->name)` resolves correctly throughout.
78
79
-**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.
79
80
-**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.
80
81
-**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.
| T22 |[Array value type tracking from loop assignments](todo/type-inference.md#t22-array-value-type-tracking-from-loop-assignments)| Medium | Medium |
28
27
| T23 |[`class-string<T>` static method dispatch](todo/type-inference.md#t23-class-stringt-static-method-dispatch)| Medium | Medium |
29
28
| T21 |[Bidirectional template inference](todo/type-inference.md#t21-bidirectional-template-inference-upperlower-bounds) (closure return type → generic) | Medium | Medium-High |
0 commit comments