Skip to content

Commit fe446e8

Browse files
committed
Update roadmap
1 parent 31cf3e4 commit fe446e8

5 files changed

Lines changed: 380 additions & 31 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ PHPantom focuses on deep type intelligence. Here's how it compares:
2323
| Linked editing ||||||
2424
| Extras<sup>2</sup> || 💰 | 🚧 | 🚧 ||
2525
| **Diagnostics** | | | | | |
26-
| PHPStan integration |||| | |
26+
| PHPStan integration |||| 🚧 | 🚧 |
2727
| Undefined variable || 💰 ||||
2828
| Type errors || 💰 || 🚧 ||
2929
| Unused variable ||||||

docs/todo.md

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,36 +27,42 @@ within the same impact tier.
2727

2828
## Sprint 5 — Polish for office adoption
2929

30-
| # | Item | Impact | Effort |
31-
| --- | ------------------------------------------------------------------------------------------------ | ------ | ------ |
32-
| | Clear [refactoring gate](todo/refactor.md) |||
33-
| D8 | [Undefined variable diagnostic](todo/diagnostics.md#d8-undefined-variable-diagnostic) | High | Medium |
34-
| D11 | [Invalid class-like kind in context](todo/diagnostics.md#d11-invalid-class-like-kind-in-context) | Medium | Low |
35-
| | **Release 0.8.0** | | |
30+
| # | Item | Impact | Effort |
31+
| --- | ---------------------------------------------------------------------------------------------------------------------------- | ------ | ------ |
32+
| | Clear [refactoring gate](todo/refactor.md) |||
33+
| D8 | [Undefined variable diagnostic](todo/diagnostics.md#d8-undefined-variable-diagnostic) | High | Medium |
34+
| D12 | [Mago linter integration](todo/diagnostics.md#d12-mago-linter-integration-optional-diagnostics) | Medium | Medium |
35+
| D11 | [Invalid class-like kind in context](todo/diagnostics.md#d11-invalid-class-like-kind-in-context) | Medium | Low |
36+
| F4 | [Return type and closure parameter type inlay hints](todo/lsp-features.md#f4-return-type-and-closure-parameter-type-inlay-hints) | Medium | Medium |
37+
| A36 | [Import all missing classes](todo/actions.md#a36-import-all-missing-classes) (bulk import) | Medium | Low |
38+
| | **Release 0.8.0** | | |
3639

3740
> **Note:** F1 (Workspace symbol search), F2 (Document symbols), A8
3841
> (Implement interface methods), A9 (Auto import), D1 (Unknown class
3942
> diagnostic), and D3 (Unknown member diagnostic) were originally
4043
> planned here but have already shipped.
4144
42-
## Sprint 6 — Type intelligence depth
45+
## Sprint 6 — Type intelligence & refactoring depth
4346

44-
| # | Item | Impact | Effort |
45-
| --- | ------------------------------------------------------------------------------------------------------------------- | ------ | ------ |
46-
| | Clear [refactoring gate](todo/refactor.md) |||
47-
| C2 | [`#[ArrayShape]` return shapes on stub functions](todo/completion.md#c2-arrayshape-return-shapes-on-stub-functions) | Medium | Medium |
48-
| T7 | [`key-of<T>` and `value-of<T>` resolution](todo/type-inference.md#t7-key-oft-and-value-oft-resolution) | Medium | Medium |
47+
| # | Item | Impact | Effort |
48+
| --- | ------------------------------------------------------------------------------------------------------------------- | ---------- | ------ |
49+
| | Clear [refactoring gate](todo/refactor.md) |||
50+
| C2 | [`#[ArrayShape]` return shapes on stub functions](todo/completion.md#c2-arrayshape-return-shapes-on-stub-functions) | Medium | Medium |
51+
| T7 | [`key-of<T>` and `value-of<T>` resolution](todo/type-inference.md#t7-key-oft-and-value-oft-resolution) | Medium | Medium |
52+
| A3 | Switch → match conversion | Low-Medium | Medium |
53+
| A10 | [Generate interface from class](todo/actions.md#a10-generate-interface-from-class) | Low-Medium | Medium |
54+
| A35 | [Convert to arrow function](todo/actions.md#a35-convert-to-arrow-function) (only non-void single-expression closures) | Low-Medium | Low |
4955

5056
## Sprint 7 — Laravel excellence & stub accuracy
5157

52-
| # | Item | Impact | Effort |
53-
| --- | --------------------------------------------------------------------------------------------- | ----------- | ----------- |
54-
| | Clear [refactoring gate](todo/refactor.md) |||
55-
| L1 | [Eloquent model attribute completion](todo/laravel.md#l1-eloquent-model-attribute-completion) | High | High |
56-
| L5 | [Blade component tag completion](todo/laravel.md#l5-blade-component-tags) | Medium | Medium-High |
57-
| E1 | [External stub packages (ide-helper, etc.)](todo/external-stubs.md#e1-external-stub-packages) | Medium-High | Medium |
58-
| E4 | [Stub version alignment with target PHP](todo/external-stubs.md#e4-stub-version-alignment) | Medium | Medium |
59-
| E5 | [Extension stub coverage audit](todo/external-stubs.md#e5-extension-stub-audit) | Medium | Low |
58+
| # | Item | Impact | Effort |
59+
| --- | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | ----------- |
60+
| | Clear [refactoring gate](todo/refactor.md) |||
61+
| L1 | [Facade completion](todo/laravel.md#l1-facade-completion) | High | High |
62+
| L11 | [Relation dot-notation string and column name string completion](todo/laravel.md#l11-relation-dot-notation-string-completion-and-column-name-string-completion) | Medium-High | Medium-High |
63+
| E1 | [External stub packages (ide-helper, etc.)](todo/external-stubs.md#e1-external-stub-packages) | Medium-High | Medium |
64+
| E4 | [Stub version alignment with target PHP](todo/external-stubs.md#e4-stub-version-alignment) | Medium | Medium |
65+
| E5 | [Extension stub coverage audit](todo/external-stubs.md#e5-extension-stub-audit) | Medium | Low |
6066

6167
## Sprint 8 — Blade support
6268

@@ -99,21 +105,18 @@ unlikely to move the needle for most users.
99105
| T10 | [Ternary expression as RHS of list destructuring](todo/type-inference.md#t10-ternary-expression-as-rhs-of-list-destructuring) | Low | Low-Medium |
100106
| T11 | [Nested list destructuring](todo/type-inference.md#t11-nested-list-destructuring) | Low | Low-Medium |
101107
| | **[Diagnostics](todo/diagnostics.md)** | | |
102-
| D5 | Diagnostic suppression intelligence | Medium | Medium |
103-
| D12 | [Mago linter integration](todo/diagnostics.md#d12-mago-linter-integration-optional-diagnostics) (optional, ~159 AST-only lint rules) | Medium | Medium |
104-
| D6 | [Unreachable code diagnostic](todo/diagnostics.md#d6-unreachable-code-diagnostic) | Low-Medium | Low |
108+
| D5 | Diagnostic suppression intelligence | Medium | Medium |
109+
| D6 | [Unreachable code diagnostic](todo/diagnostics.md#d6-unreachable-code-diagnostic) | Low-Medium | Low |
105110
| D10 | PHPMD diagnostic proxy | Low | Medium |
106111
| | **[Bug Fixes](todo/bugs.md)** | | |
107112
| | **[Code Actions](todo/actions.md)** | | |
108-
| A8 | [Update docblock to match signature](todo/actions.md#a8-update-docblock-to-match-signature) | Medium | Medium |
113+
| A8 | [Update docblock to match signature](todo/actions.md#a8-update-docblock-to-match-signature) | Medium | Medium |
109114
| A16 | [Snippet placeholder for extracted method name](todo/actions.md#a16-snippet-placeholder-for-extracted-method-name) (lets the user type over the generated name immediately) | Medium | Low-Medium |
110-
| A25 | [`strpos``str_contains`](todo/actions.md#a25-strpos--str_contains-php-80) (PHP 8.0+) | Medium | Low |
111-
| A28 | [Explicit nullable parameter type](todo/actions.md#a28-explicit-nullable-parameter-type-php-84-deprecation) (PHP 8.4 deprecation) | Medium | Low |
112-
| A10 | [Generate interface from class](todo/actions.md#a10-generate-interface-from-class) | Low-Medium | Medium |
113-
| A29 | [Simplify boolean return](todo/actions.md#a29-simplify-boolean-return) (`if (cond) return true; return false;``return cond;`) | Low-Medium | Medium |
114-
| A31 | [Remove always-else](todo/actions.md#a31-remove-always-else-extract-guard-clause) (extract guard clause) | Low-Medium | Medium |
115+
| A25 | [`strpos``str_contains`](todo/actions.md#a25-strpos--str_contains-php-80) (PHP 8.0+) | Medium | Low |
116+
| A28 | [Explicit nullable parameter type](todo/actions.md#a28-explicit-nullable-parameter-type-php-84-deprecation) (PHP 8.4 deprecation) | Medium | Low |
117+
| A29 | [Simplify boolean return](todo/actions.md#a29-simplify-boolean-return) (`if (cond) return true; return false;``return cond;`) | Low-Medium | Medium |
118+
| A31 | [Remove always-else](todo/actions.md#a31-remove-always-else-extract-guard-clause) (extract guard clause) | Low-Medium | Medium |
115119
| A34 | [Unified code action handler architecture](todo/actions.md#a34-unified-code-action-handler-architecture) (closure-based resolve, unified fix type) | Medium | Medium-High |
116-
| A3 | Switch → match conversion | Low | Medium |
117120
| | **[PHPStan Code Actions](todo/phpstan-actions.md)** | | |
118121
| H4 | `assign.byRefForeachExpr` — unset by-reference foreach variable | Medium | Medium |
119122
| H13 | `property.notFound` — declare missing property (same-class) | Medium | Medium |
@@ -131,6 +134,7 @@ unlikely to move the needle for most users.
131134
| FX5 | [`phpstan.property.unused` / `phpstan.method.unused` — remove unused member](todo/fix-cli.md#fx5-phpstanpropertyunused--phpstanmethodunused--remove-unused-member) | Low | Low |
132135
| FX6 | [`phpstan.generics.callSiteVarianceRedundant` — remove redundant variance](todo/fix-cli.md#fx6-phpstangenericscallsitevarianceredundant--remove-redundant-variance) | Low | Low |
133136
| | **[LSP Features](todo/lsp-features.md)** | | |
137+
| F5 | [Call hierarchy](todo/lsp-features.md#f5-call-hierarchy) (incoming/outgoing calls) | Medium | Medium |
134138
| F2 | [Partial result streaming via `$/progress`](todo/lsp-features.md#f2-partial-result-streaming-via-progress) | Medium | Medium-High |
135139
| F3 | Incremental text sync | Low-Medium | Medium |
136140
| | **[Signature Help](todo/signature-help.md)** | | |

docs/todo/actions.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,3 +388,72 @@ these eagerly, removing the else changes semantics).
388388

389389
**Code action kind:** `refactor.rewrite`.
390390

391+
---
392+
393+
### A35. Convert to arrow function
394+
395+
**Impact: Low-Medium · Effort: Low**
396+
397+
Convert a single-expression anonymous function to an arrow function:
398+
`function($x) { return $x * 2; }``fn($x) => $x * 2`.
399+
400+
Only offer the conversion when ALL of the following are true:
401+
402+
- The closure body contains exactly one statement, and that statement
403+
is a `return` with an expression.
404+
- The closure does not have a `use()` clause that captures by reference
405+
(`&$var`). Arrow functions capture by value automatically; by-ref
406+
semantics differ.
407+
- The closure's return type (native hint, `@return` docblock, or
408+
inferred from callable context) is NOT `void` or `never`. Arrow
409+
functions always return their expression value. Converting a
410+
`void`-returning closure produces code that silently changes
411+
behaviour when the caller inspects the return value (e.g.
412+
`array_walk`, `register_shutdown_function`, `Event::listen`).
413+
- The closure does not have a return type hint of `void` or `never`
414+
explicitly declared.
415+
- `php_version >= 7.4`.
416+
417+
**Why the void/never guard matters:** Several PHP functions and
418+
framework APIs behave differently depending on whether a callback
419+
returns a value. `array_walk` ignores the return, but `array_filter`
420+
uses it. A closure typed `function(): void { doSomething(); }` makes
421+
the intent explicit. Converting it to `fn() => doSomething()` changes
422+
the return value from `null` (void) to whatever `doSomething()` returns,
423+
which can silently break filtering, mapping, or event-handling logic.
424+
425+
**Code action kind:** `refactor.rewrite`.
426+
427+
---
428+
429+
### A36. Import all missing classes
430+
431+
**Impact: Medium · Effort: Low**
432+
433+
Bulk code action that imports all unresolvable class names in a file at
434+
once, instead of requiring the user to trigger "Import class" on each
435+
name individually.
436+
437+
### Behaviour
438+
439+
- **Trigger:** File contains one or more unresolved class names (the
440+
same condition that triggers the existing single-class import action).
441+
The bulk action appears in the source action menu.
442+
- **Code action kind:** `source.organizeImports` or
443+
`source.importAll`.
444+
- **Result:** For each unresolved class name, resolve candidates using
445+
the same logic as the existing import action. When exactly one
446+
candidate exists, import it. When multiple candidates exist, pick the
447+
one with the highest namespace affinity (same ranking the single
448+
import action uses). Insert all new `use` statements in alphabetical
449+
order.
450+
451+
### Edge cases
452+
453+
- If any class name has zero candidates (truly unknown), skip it
454+
silently.
455+
- If two unresolved names would import different classes with the same
456+
short name, import the first and skip the second (a conflict that
457+
requires manual resolution).
458+
- Already-imported names are excluded.
459+

0 commit comments

Comments
 (0)