Skip to content

Commit 31cf3e4

Browse files
committed
Implement "Remove unused return type" code action for PHPStan, and
update general docs
1 parent dec5a3e commit 31cf3e4

18 files changed

Lines changed: 2280 additions & 249 deletions

docs/CHANGELOG.md

Lines changed: 66 additions & 97 deletions
Large diffs are not rendered by default.

docs/CLI.md

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,15 @@ editor, so results are consistent.
1414
| `phpantom_lsp init` | Generate a default `.phpantom.toml` config file |
1515

1616
Running with no subcommand starts the language server. Editors launch
17-
this automatically; you never need to run it by hand.
17+
this automatically.
1818

1919
---
2020

21-
## `analyze` — Batch diagnostics
21+
## `analyze`
2222

23-
Scans PHP files and reports PHPantom's own diagnostics (no PHPStan, no
24-
external tools) in a PHPStan-style table format. Use it to find spots
25-
where the LSP cannot resolve a symbol, so you can achieve full
26-
completion coverage.
23+
Scans PHP files and reports PHPantom diagnostics in a PHPStan-style
24+
table format. Use it to find spots where the LSP cannot resolve a
25+
symbol, so you can achieve full completion coverage.
2726

2827
```sh
2928
phpantom_lsp analyze # scan entire project
@@ -83,11 +82,10 @@ Each has a rule identifier shown below the message.
8382

8483
---
8584

86-
## `fix` — Automated code fixes
85+
## `fix`
8786

88-
Applies code fixes across the project, modeled after
89-
[php-cs-fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer). Specify
90-
which rules to run, or omit `--rule` to run all preferred native fixers.
87+
Applies code fixes across the project. Specify which rules to run, or
88+
omit `--rule` to run all preferred native fixers.
9189

9290
```sh
9391
phpantom_lsp fix # apply all preferred fixers
@@ -106,7 +104,7 @@ phpantom_lsp fix --project-root /path/to/app # explicit project root
106104
| `[PATH]` | File or directory to fix. Defaults to the entire project. |
107105
| `--rule <RULE>` | Rule to apply (repeatable). Omit to run all preferred native rules. |
108106
| `--dry-run` | Report what would change without writing files. |
109-
| `--with-phpstan` | Enable PHPStan-based fixers (runs PHPStan to collect diagnostics). |
107+
| `--with-phpstan` | Enable PHPStan-based fixers (future feature). |
110108
| `--project-root <DIR>` | Project root directory. Defaults to the current working directory. |
111109
| `--no-colour` | Disable ANSI colour output. |
112110

@@ -120,28 +118,11 @@ phpantom_lsp fix --project-root /path/to/app # explicit project root
120118

121119
### Available rules
122120

123-
Rules correspond to diagnostic identifiers. Native rules run without
124-
external tools. PHPStan rules require `--with-phpstan` and are prefixed
125-
with `phpstan.`.
121+
Rules correspond to diagnostic identifiers.
126122

127-
**Native rules (shipped):**
128-
129-
| Rule | Description |
130-
| ------------------ | --------------------------------------------------------- |
131-
| `unused_import` | Remove unused `use` statements. Handles simple imports, group imports (removes individual members), and collapses leftover blank lines. |
132-
133-
**PHPStan rules (planned, requires `--with-phpstan`):**
134-
135-
These are not yet implemented. See the [fix CLI roadmap](todo/fix-cli.md)
136-
for details.
137-
138-
| Rule | Description |
139-
| -------------------------------------------- | --------------------------------------------------- |
140-
| `phpstan.return.unusedType` | Remove unused type from a return union |
141-
| `phpstan.missingType.iterableValue` | Add `@return array<mixed>` for untyped iterables |
142-
| `phpstan.property.unused` | Remove unused property declarations |
143-
| `phpstan.method.unused` | Remove unused method declarations |
144-
| `phpstan.generics.callSiteVarianceRedundant` | Remove redundant variance annotations in docblocks |
123+
| Rule | Description |
124+
| ------------------ | ------------------------------ |
125+
| `unused_import` | Remove unused `use` statements |
145126

146127
### Example output
147128

@@ -179,7 +160,7 @@ Use `--dry-run` in CI to enforce that imports stay clean:
179160

180161
```sh
181162
phpantom_lsp fix --dry-run --rule unused_import --project-root .
182-
# Exit code 2 means fixable issues exist → fail the build.
163+
# Exit code 2 means fixable issues exist. Fail the build.
183164
```
184165

185166
### Idempotency
@@ -190,7 +171,7 @@ writes nothing.
190171

191172
---
192173

193-
## `init` — Generate config file
174+
## `init`
194175

195176
Creates a default `.phpantom.toml` in the current directory with all
196177
options documented and commented out. Safe to run if the file already

docs/SETUP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ PHPantom supports an optional per-project configuration file for settings like P
150150
To generate a default config file with all options documented and commented out:
151151

152152
```bash
153-
phpantom_lsp --init
153+
phpantom_lsp init
154154
```
155155

156156
This creates a `.phpantom.toml` in the current directory. Currently supported settings:

docs/todo.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ within the same impact tier.
2323

2424
| # | Item | Impact | Effort |
2525
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- |
26-
| H10 | [`return.unusedType` — remove unused type from return union](todo/phpstan-actions.md#h10-returnunusedtype--remove-unused-type-from-return-union) | Medium | Medium |
27-
| H6 | `return.type` — update return type to match actual returns | Medium | Medium |
2826
| | **Release 0.7.0** | | |
2927

3028
## Sprint 5 — Polish for office adoption

docs/todo/phpstan-actions.md

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -13,74 +13,6 @@ No outstanding items.
1313

1414
## Tier 1 — Simple message parsing
1515

16-
### H6. `return.type` — Update return type to match actual returns
17-
18-
**Identifier:** `return.type`
19-
**Messages:**
20-
- `Method Foo::bar() should return {expected} but returns {actual}.`
21-
- `Function foo() should return {expected} but returns {actual}.`
22-
- `Anonymous function should return {expected} but returns {actual}.`
23-
24-
Parse `{actual}` from the message with regex:
25-
`should return (.+) but returns (.+)\.$`
26-
27-
Offer two quickfixes:
28-
29-
1. **Update native return type** — find the `: Type` after the parameter list
30-
and replace `Type` with the actual type. Only offer this when the actual
31-
type is a valid native PHP type (scalars, class names, `null`, simple
32-
unions on PHP 8.0+, intersection types on PHP 8.1+).
33-
2. **Update `@return` tag** — if a docblock with `@return` exists, replace the
34-
type. If no docblock exists, create one with `@return {actual}`.
35-
36-
Mark neither as `is_preferred` since the right fix might be to change the code
37-
rather than the signature.
38-
39-
**Inheritance guard:** Before offering the native return type quickfix, check
40-
whether the method overrides a parent class or interface method. Changing the
41-
return type could violate LSP (Liskov Substitution Principle) if the parent
42-
declares a wider type. Rector's `ClassMethodReturnTypeOverrideGuard` enforces
43-
this same constraint. When the method overrides a parent, only offer the
44-
docblock quickfix (docblock types don't affect runtime covariance checks).
45-
46-
**All-paths completeness:** When the `{actual}` type is inferred from return
47-
statements, verify that all return paths yield a consistent type. If some
48-
paths return a value and others fall through without returning, the actual
49-
type should include `void` or `null`. Rector's return type rules universally
50-
bail out when return paths are ambiguous rather than risk an incorrect
51-
declaration.
52-
53-
**Stale detection:** difficult to do precisely because PHPStan's type syntax
54-
differs from source syntax. As a heuristic, check whether the return type
55-
declaration or `@return` tag was modified since the diagnostic was issued
56-
(i.e. the text on the relevant line no longer matches the `{expected}` type).
57-
Or skip stale detection for this one — the `@phpstan-ignore` stale check
58-
already covers the suppress-with-comment path.
59-
60-
---
61-
62-
### H10. `return.unusedType` — Remove unused type from return union
63-
64-
**Identifier:** `return.unusedType`
65-
**Messages:**
66-
- `Method Foo::bar() never returns {type} so it can be removed from the return type.`
67-
- `Function foo() never returns {type} so it can be removed from the return type.`
68-
69-
Parse `{type}` from the message. Find the return type (native or `@return`),
70-
parse the union/intersection, remove the unused member, and rewrite.
71-
72-
For native types: `string|null` with unused `null` becomes `string`.
73-
For docblock types: same logic on the `@return` tag.
74-
Handle intersection types too: `Foo&Bar` with unused `Bar` becomes `Foo`.
75-
76-
If removing the type would leave a single-member union, simplify
77-
(e.g. `string|null` minus `null` becomes `string`).
78-
79-
**Stale detection:** the return type no longer contains `{type}` as a union
80-
or intersection member.
81-
82-
---
83-
8416
### H4. `assign.byRefForeachExpr` — Unset by-reference foreach variable
8517

8618
**Identifier:** `assign.byRefForeachExpr`

src/code_actions/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,14 @@ impl Backend {
281281
"phpstan.fixReturnType.stripExpr"
282282
| "phpstan.fixReturnType.changeTypeToActual"
283283
| "phpstan.fixReturnType.changeType"
284-
| "phpstan.fixReturnType.addType" => self.resolve_fix_return_type(&data, &content),
284+
| "phpstan.fixReturnType.addType"
285+
| "phpstan.fixReturnType.updateReturnType" => {
286+
self.resolve_fix_return_type(&data, &content)
287+
}
288+
// ── Remove unused return type ────────────────────────────
289+
"phpstan.removeUnusedReturnType" => {
290+
self.resolve_remove_unused_return_type(&data, &content)
291+
}
285292
// ── Add iterable return type ────────────────────────────
286293
"phpstan.addIterableType" => self.resolve_add_iterable_type(&data, &content),
287294
// ── Remove unreachable statement ────────────────────────

0 commit comments

Comments
 (0)