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/ARCHITECTURE.md
+25Lines changed: 25 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -193,6 +193,31 @@ Variable go-to-definition (`$var` → jump to definition) uses two layers:
193
193
1.**Symbol map** (`var_defs`): the primary path. Finds the most recent `VarDefSite` before the cursor within the enclosing scope. When the cursor is physically on a definition token (parameter, foreach binding, catch variable), it returns `None` so the caller can fall through to type-hint resolution.
194
194
2.**AST-based search** (`resolve_variable_definition_ast`): parses the file and walks the enclosing scope to find the definition site. Handles destructuring (`[$a, $b] = ...`, `list($a, $b) = ...`) and nested scopes correctly. Used as a fallback when the symbol map doesn't have a match. Returns `None` when the AST parse fails.
195
195
196
+
## Go-to-Type-Definition Architecture
197
+
198
+
"Go to Type Definition" (`textDocument/typeDefinition`) jumps from a symbol to the class declaration of its **resolved type**, rather than to the definition site. For example, if `$user` is typed as `User`, go-to-definition jumps to the `$user = ...` assignment, while go-to-type-definition jumps to `class User { … }`.
199
+
200
+
The handler (`definition/type_definition.rs`) reuses the same symbol-map lookup as go-to-definition and the same type-resolution pipelines as hover and completion:
201
+
202
+
1.**Symbol map lookup.** Convert the cursor position to a byte offset and binary-search the symbol map (same as go-to-definition, including the offset-1 retry for end-of-token cursors).
203
+
204
+
2.**Type resolution by symbol kind:**
205
+
206
+
|`SymbolKind`| Resolution path |
207
+
|---|---|
208
+
|`Variable`|`resolve_variable_type_string` (type-string path) with fallback to `resolve_variable_types` (ClassInfo path). `$this` resolves to the enclosing class. |
209
+
|`MemberAccess`|`resolve_target_classes` finds the subject's class, then the method's `return_type` or the property's `type_hint` is extracted. `self`/`static`/`$this` in return types are replaced with the owning class name. |
210
+
|`SelfStaticParent`|`self`/`static` resolve to the enclosing class; `parent` resolves to the parent class. |
211
+
|`ClassReference`| The type is the class itself. |
212
+
|`FunctionCall`| The function's `return_type` is extracted. |
213
+
|`ClassDeclaration`, `MemberDeclaration`, `ConstantReference`| No type definition target; returns `None`. |
214
+
215
+
3.**Type string to class names.**`extract_class_names_from_type_string` splits union types at depth-0 `|` separators, strips `?` (nullable), generic parameters (`<…>`), array shapes (`{…}`), and trailing `[]`. Scalar types (`int`, `string`, `array`, `void`, `mixed`, `bool`, `float`, `null`, `false`, `true`, `never`, `callable`, `iterable`, `resource`, `object`) are excluded since they have no user-navigable declaration.
216
+
217
+
4.**Class name to location.** Each surviving class name is resolved via `resolve_class_reference` (the same function go-to-definition uses for class references), which tries same-file AST lookup, cross-file `class_index` + `ast_map`, Composer classmap, PSR-4, and template parameter fallback. Duplicate locations are deduplicated.
218
+
219
+
For union types, the handler returns multiple locations (one per non-scalar class in the union), which editors display as a peek list.
220
+
196
221
## Signature Help Architecture
197
222
198
223
Signature help shows parameter hints when the cursor is inside the parentheses
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
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
28
28
-**Interface template inheritance.** When a class implements an interface whose methods use `@template`, `@param class-string<T>`, or `@return T`, the implementing class's overridden methods now inherit the template parameters, bindings, conditional return types, and type assertions.
29
29
-**Generic `@phpstan-assert` with `class-string<T>`.**`@phpstan-assert T $value` combined with a `@template T` bound via `class-string<T>` now resolves the narrowed type from the call-site argument. Also works on static method calls like `Assert::instanceOf($value, Foo::class)`.
30
30
-**Property-level narrowing.**`if ($this->prop instanceof Foo)` now narrows the type of `$this->prop` inside the then-body, else-body, and after guard clauses. `assert($this->prop instanceof Foo)` also works. Previously only plain variables participated in instanceof narrowing.
31
+
-**Go to Type Definition.**`textDocument/typeDefinition` jumps from a variable, property access, method call, or function call to the class declaration of its resolved type. For example, if `$user` is typed as `User`, go-to-definition jumps to the assignment while go-to-type-definition jumps to `class User`. Union types produce multiple locations (one per class). Scalar types are filtered out. Works across files via PSR-4 resolution.
31
32
-**Inline `&&` short-circuit narrowing.** In `if ($x instanceof Foo && $x->bar())`, the right-hand side of `&&` now sees the narrowed type from the left-hand side. Previously narrowing only applied inside the if body, so method calls within the condition itself were checked against the original (broader) type.
32
33
-**Compound negated guard clause narrowing.** After `if (!$x instanceof A && !$x instanceof B) { return; }`, the surviving code narrows `$x` to `A|B`. Previously only single negated instanceof guard clauses were recognized.
33
34
-**Invoked closure and arrow function return types.**`(fn(): Foo => new Foo())()` and `(function(): Bar { return new Bar(); })()` now resolve to the return type of the closure or arrow function.
0 commit comments