|
53 | 53 | │ # Data model |
54 | 54 | ├── types/ # Core structures (ClassInfo, MethodInfo, ResolvedType, SubjectExpr, …) |
55 | 55 | ├── php_type/ # PHP type representation: parse, display, normalize, subtype, transform |
56 | | -├── subject_expr.rs, subject_extraction.rs, subject_resolution.rs |
57 | | -│ # Extracting and resolving the subject before ->, ?->, :: |
58 | 56 | │ |
59 | 57 | │ # Parsing & indexing |
60 | 58 | ├── parser/ # mago-syntax AST → ClassInfo/FunctionInfo (classes, functions, use statements, attributes, incremental update_ast) |
|
74 | 72 | ├── stubs.rs, stub_patches.rs # Embedded phpstorm-stubs index + hand patches |
75 | 73 | ├── phar.rs # Class discovery inside PHAR archives |
76 | 74 | │ |
77 | | -│ # The shared type engine (lives under completion/, see note below) |
78 | | -├── completion/ |
| 75 | +│ # The shared type engine ("what is the type of this expression here?") |
| 76 | +├── type_engine/ |
| 77 | +│ ├── subject_expr.rs, subject_extraction.rs, subject_resolution.rs |
| 78 | +│ │ # Extracting and resolving the subject before ->, ?->, :: |
79 | 79 | │ ├── resolver/ # Subject expression → ClassInfo (type engine entry point) + chain cache, property narrowing |
80 | 80 | │ ├── variable/ # Variable type resolution via assignment scanning |
81 | 81 | │ │ ├── forward_walk/ # the forward walker (shared by diagnostics, hover, go-to-def, sig help) |
82 | 82 | │ │ ├── rhs_resolution/ # right-hand-side expression resolution |
83 | 83 | │ │ └── … # closure/foreach/class-string/raw-type resolution |
84 | 84 | │ ├── call_resolution/ # Call expression + callable target resolution, return types, arg types |
85 | | -│ ├── types/ # Type-hint → ClassInfo, narrowing/, conditional return types |
86 | | -│ │ # ---- completion-specific below ---- |
| 85 | +│ └── types/ # Type-hint → ClassInfo, narrowing/, conditional return types |
| 86 | +│ |
| 87 | +│ # Completion (uses the type engine above) |
| 88 | +├── completion/ |
87 | 89 | │ ├── handler/ # Completion request orchestration (member access, class constants, named args, phpdoc) |
88 | 90 | │ ├── context/ # Context-specific completion (catch, class, keyword, namespace, override, type-hint, …) |
89 | 91 | │ ├── phpdoc/ # PHPDoc tag completion + docblock generation/ |
90 | 92 | │ ├── source/ # Source-text scanning (comment position, throws analysis) |
| 93 | +│ ├── variable/ # Variable-name completion + scope collection |
91 | 94 | │ ├── builder.rs # Build LSP CompletionItems from resolved ClassInfo |
92 | 95 | │ └── … # target.rs, array_shape.rs, named_args.rs, use_edit.rs, eloquent_string.rs, laravel_route_controller.rs, … |
93 | 96 | │ |
@@ -127,16 +130,15 @@ tests/ |
127 | 130 | └── assert_type_runner.rs, fixture_runner.rs |
128 | 131 | ``` |
129 | 132 |
|
130 | | -**The shared type engine lives under `completion/`, despite the name.** |
131 | | -`completion/resolver/`, `completion/variable/` (including the |
132 | | -`forward_walk/` forward walker), `completion/call_resolution/`, and |
133 | | -`completion/types/` are the project's single type-resolution engine — |
134 | | -they answer "what is the type of this expression here?" and are consumed |
135 | | -by diagnostics, hover, go-to-definition, and signature help, not just |
136 | | -completion (see [Forward Walker](#forward-walker) and |
137 | | -[Name Resolution](#name-resolution) below). This naming is a known wart; |
138 | | -extracting the engine into its own top-level module is tracked in |
139 | | -[`docs/todo/refactor.md`](todo/refactor.md). |
| 133 | +**The shared type engine lives under `type_engine/`.** `type_engine/resolver/`, |
| 134 | +`type_engine/variable/` (including the `forward_walk/` forward walker), |
| 135 | +`type_engine/call_resolution/`, `type_engine/types/`, and the subject |
| 136 | +extraction/resolution files are the project's single type-resolution engine — |
| 137 | +they answer "what is the type of this expression here?" and are consumed by |
| 138 | +diagnostics, hover, go-to-definition, and signature help, not just completion |
| 139 | +(see [Forward Walker](#forward-walker) and [Name Resolution](#name-resolution) |
| 140 | +below). Do not build a second type-resolution path: extend the engine here so |
| 141 | +every consumer benefits. |
140 | 142 |
|
141 | 143 | ## External Crates |
142 | 144 |
|
@@ -514,7 +516,7 @@ Current patches: |
514 | 516 |
|
515 | 517 | This is analogous to the [Laravel Class Patches](#laravel-class-patches) system but for built-in PHP functions rather than framework classes. When phpstorm-stubs gains proper annotations for a patched function, the corresponding patch can be deleted. |
516 | 518 |
|
517 | | -**When to add a patch here vs. hardcoded logic in `rhs_resolution.rs`:** if the correct behaviour can be expressed with `@template` / `@return` annotations (i.e. PHPStan's own stubs already have the fix), it belongs in `stub_patches.rs`. If the behaviour requires inspecting call-site argument *values* at resolution time (e.g. `array_map`'s callback return type, or `array_filter` preserving the input array's element type), it must stay as hardcoded logic in `rhs_resolution.rs` / `raw_type_inference.rs`. Those functions are tracked in `ARRAY_PRESERVING_FUNCS` and `ARRAY_ELEMENT_FUNCS` in `completion/variable/mod.rs`, with the full inventory in `docs/todo/completion.md` C1. |
| 519 | +**When to add a patch here vs. hardcoded logic in `rhs_resolution.rs`:** if the correct behaviour can be expressed with `@template` / `@return` annotations (i.e. PHPStan's own stubs already have the fix), it belongs in `stub_patches.rs`. If the behaviour requires inspecting call-site argument *values* at resolution time (e.g. `array_map`'s callback return type, or `array_filter` preserving the input array's element type), it must stay as hardcoded logic in `rhs_resolution.rs` / `raw_type_inference.rs`. Those functions are tracked in `ARRAY_PRESERVING_FUNCS` and `ARRAY_ELEMENT_FUNCS` in `type_engine/variable/mod.rs`, with the full inventory in `docs/todo/completion.md` C1. |
518 | 520 |
|
519 | 521 | #### Class patches |
520 | 522 |
|
@@ -636,9 +638,9 @@ Scope methods (both `scopeX` convention and `#[Scope]` attribute) are synthesize |
636 | 638 |
|
637 | 639 | Instead, scope injection happens in three places: |
638 | 640 |
|
639 | | -1. **Post-generic-substitution hook** (`completion/resolver.rs`, inside `type_hint_to_classes_depth`): after `resolve_class_fully` + `apply_generic_args` produces a `Builder<User>` class, the resolver detects that the result is an Eloquent Builder with a concrete model generic argument. It calls `build_scope_methods_for_builder(model_name, class_loader)` which loads the model, fully resolves it, extracts scope methods, and returns them as instance methods with `static` in return types substituted to the concrete model name. These are merged onto the Builder's method list, giving `$q->active()` after `$q = User::where(...)`. |
| 641 | +1. **Post-generic-substitution hook** (`type_engine/resolver/`, inside `type_hint_to_classes_depth`): after `resolve_class_fully` + `apply_generic_args` produces a `Builder<User>` class, the resolver detects that the result is an Eloquent Builder with a concrete model generic argument. It calls `build_scope_methods_for_builder(model_name, class_loader)` which loads the model, fully resolves it, extracts scope methods, and returns them as instance methods with `static` in return types substituted to the concrete model name. These are merged onto the Builder's method list, giving `$q->active()` after `$q = User::where(...)`. |
640 | 642 |
|
641 | | -2. **Scope body Builder enrichment** (`completion/variable_resolution.rs`, `enrich_builder_type_in_scope`): inside a scope method body, the `$query` parameter is typically typed as `Builder` without generic arguments. The enrichment function detects when the enclosing method is a scope (either the `scopeX` naming convention or the `#[Scope]` attribute) on a class that extends Eloquent Model, and the parameter type is `Builder` without generics. It rewrites the type to `Builder<EnclosingModel>`, which then flows through the generic-args path and triggers the post-substitution hook above. This makes `$query->otherScope()` resolve inside scope bodies. |
| 643 | +2. **Scope body Builder enrichment** (`type_engine/variable/resolution.rs`, `enrich_builder_type_in_scope`): inside a scope method body, the `$query` parameter is typically typed as `Builder` without generic arguments. The enrichment function detects when the enclosing method is a scope (either the `scopeX` naming convention or the `#[Scope]` attribute) on a class that extends Eloquent Model, and the parameter type is `Builder` without generics. It rewrites the type to `Builder<EnclosingModel>`, which then flows through the generic-args path and triggers the post-substitution hook above. This makes `$query->otherScope()` resolve inside scope bodies. |
642 | 644 |
|
643 | 645 | 3. **Go-to-definition fallback** (`definition/member.rs`, `find_scope_on_builder_model`): when go-to-definition resolves a member on a Builder class and the normal lookup chain (own members, traits, parents, mixins, builder forwarding) fails, this fallback checks whether the member is a scope method injected from the model. It confirms the resolved candidate (with injected scopes) has the method, extracts the model name from the scope method's return type generic argument, loads the model, and finds the declaration through the model's inheritance chain. For `scopeX`-style scopes it looks for `scopeXxx`; for `#[Scope]`-attributed methods it falls back to the original method name. This bridges the gap between completion (which works on the merged ClassInfo) and GTD (which traces back to the declaring source file). |
644 | 646 |
|
@@ -1112,7 +1114,7 @@ Everything above diagnoses files the user has opened. `src/diagnostics/workspace |
1112 | 1114 |
|
1113 | 1115 | ## Forward Walker |
1114 | 1116 |
|
1115 | | -The forward walker (`src/completion/variable/forward_walk.rs`) walks |
| 1117 | +The forward walker (`src/type_engine/variable/forward_walk/`) walks |
1116 | 1118 | method bodies top-to-bottom, building a scope of variable types at |
1117 | 1119 | each statement boundary. It is shared by diagnostics, completion, |
1118 | 1120 | hover, go-to-definition, and signature help. |
|
0 commit comments