Skip to content

Commit a1beb6d

Browse files
committed
Add bulk "Import all missing classes" code action
1 parent 092c292 commit a1beb6d

9 files changed

Lines changed: 1298 additions & 220 deletions

File tree

docs/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
- **Invalid class-like kind diagnostics.** Flags class-like names used in positions where their kind is guaranteed to fail at runtime: `new` on abstract classes, interfaces, traits, or enums; `extends` on a final class, interface, or trait; `implements` with a non-interface; trait `use` with a non-trait; `instanceof` with a trait (always false); `catch` with a non-Throwable type or trait; and traits in native type-hint positions. Severity follows PHP semantics: unconditional fatal errors are Error, runtime-conditional failures are Warning.
1515
- **Nested array shape inference from multi-level key assignments.** Assignments like `$b['a']['b'] = 'x'` now produce a nested array shape type (`array{a: array{b: string}}`), enabling array key completion and hover for arrays built incrementally with nested keys. Previously only single-level key assignments were tracked.
1616
- **Machine-readable CLI output.** Both `analyze` and `fix` accept a `--format` flag with three options: `table` (default, the existing human-readable output), `github` (GitHub Actions workflow commands that appear as inline annotations on pull request diffs), and `json` (structured output for dashboards and tooling). When no format is specified and the `GITHUB_ACTIONS` environment variable is set, table output automatically includes GitHub annotations alongside the human-readable table, matching PHPStan's behaviour.
17+
- **Import all missing classes.** A bulk code action that imports every unresolved class name in the file at once. Only names with a single unambiguous candidate are imported; ambiguous names are left for manual resolution via the single-class import action. Short-name conflicts are detected and skipped. Appears in the quick-fix menu when the cursor is on an unresolved class name and the file has two or more unresolved names.
18+
- **Context-aware import candidate filtering.** Import class actions now filter candidates by the syntactic context of the reference. After `implements` only interfaces are offered, after trait `use` only traits, after `extends` only classes or interfaces (as appropriate). This eliminates wrong-kind suggestions and reduces ambiguity for both single and bulk imports.
1719

1820
### Fixed
1921

22+
- **Missing blank line between namespace and first import.** When auto-importing a class into a file that has a namespace declaration but no existing `use` statements and no blank line after the namespace, the inserted `use` statement is now preceded by a blank line (PSR-12 convention). Files that already have a blank line after the namespace are left unchanged.
2023
- **"Remove all unused imports" missing in braced namespaces.** The bulk action now appears when the cursor is on a `use` line inside a `namespace Foo { … }` block. Previously the brace-depth heuristic treated these as trait-use statements inside a class body.
2124
- **False-positive undefined variable on nested array access assignment.** `$b['a']['a'] = 'a'` no longer flags `$b` as undefined. The scope collector now correctly propagates write context through nested `ArrayAccess` and `ArrayAppend` expressions, so the base variable is recognized as defined.
2225

docs/todo.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ within the same impact tier.
2525

2626
| # | Item | Impact | Effort |
2727
| --- | ---------------------------------------------------------------------------------------------------------------------------- | ----------- | ------ |
28-
| A36 | [Import all missing classes](todo/actions.md#a36-import-all-missing-classes) (bulk import) | Medium | Low |
2928
| D4 | [Unused variable diagnostic](todo/diagnostics.md#d4-unused-variable-diagnostic) | Medium | Medium |
3029
| D12 | [Mago linter integration](todo/diagnostics.md#d12-mago-linter-integration-optional-diagnostics) | Medium | Medium |
3130
| F4 | [Return type and closure parameter type inlay hints](todo/lsp-features.md#f4-return-type-and-closure-parameter-type-inlay-hints) | Medium | Medium |

docs/todo/actions.md

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -426,39 +426,6 @@ which can silently break filtering, mapping, or event-handling logic.
426426

427427
---
428428

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-
460-
---
461-
462429
### A37. Simplify with `?->` (nullsafe operator)
463430

464431
**Impact: Low-Medium · Effort: Medium**

example.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2550,6 +2550,9 @@ public function toArray(): array { return []; }
25502550
// Place cursor on `MutateArrayInsertSpec` and press Ctrl+. (or Cmd+. on Mac)
25512551
// to see "Import `Couchbase\MutateArrayInsertSpec`" in the quick-fix menu.
25522552
// Accepting inserts a `use Couchbase\MutateArrayInsertSpec;` at the top.
2553+
//
2554+
// Because this file has two unresolved names, the quick-fix menu also shows
2555+
// "Import all missing classes" which imports both at once.
25532556

25542557
class ImportClassDemo
25552558
{
@@ -3362,7 +3365,10 @@ private function acceptTrait(JsonSerializer $x): JsonSerializer
33623365
}
33633366

33643367
// These also produce diagnostics but would crash at class-load time,
3365-
// so they are═══════════════════════════════════════════════════════════════════════════
3368+
// so they are commented out. See the AGENTS.md hoisting pitfall note.
3369+
}
3370+
3371+
33663372
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
33673373
// ┃ SCAFFOLDING — Supporting definitions below this line. ┃
33683374

0 commit comments

Comments
 (0)