Skip to content

Make four rules auto-fixable with PHPStan --fix - #371

Draft
janedbal wants to merge 2 commits into
masterfrom
jn-autofixable-rules
Draft

janedbal wants to merge 2 commits into
masterfrom
jn-autofixable-rules

Conversation

@janedbal

@janedbal janedbal commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

PHPStan 2.3.5 adds auto-fixable rules (RuleErrorBuilder::fixNode() / removeNode(), phpstan analyse --fix). This PR adds a fix to the rules that have only one correct fix.

Rule Fix Not fixed when
forbidUnusedClosureParameters Removes the trailing unused parameters. —
forbidMatchDefaultArmForEnums Removes the default arm. The other arms do not list all cases of the native type. A PHPDoc-only narrowing does not count: the removed arm then causes UnhandledMatchError at runtime.
forbidUselessNullableReturn Removes null from the native return type (?int → int, int|string|null → int|string). The PHPDoc return type contains null, because the native type then becomes narrower than the PHPDoc type.
uselessPrivatePropertyDefaultValue Removes the default value. —

Implementation notes

  • PHPStan finds the node to fix by its class and file position, so a rule must return a real parser node.
    • forbidMatchDefaultArmForEnums now processes Match_ instead of MatchExpressionNode, because MatchExpressionNode does not expose the MatchArm nodes.
    • forbidUselessNullableReturn gets the return type node from MethodReturnStatementsNode::getClassMethod() and FunctionReturnStatementsNode::getFunction(), which Expose ClassMethod and Function_ on MethodReturnStatementsNode and FunctionReturnStatementsNode phpstan/phpstan-src#6958 adds.
    • uselessPrivatePropertyDefaultValue fixes the PropertyItem. ClassPropertyNode::getOriginalNode() returns a synthetic Property with the positions of the item, so the Property itself does not match.
  • Requires phpstan/phpstan: ^2.3.6@dev for now. The getters from Expose ClassMethod and Function_ on MethodReturnStatementsNode and FunctionReturnStatementsNode phpstan/phpstan-src#6958 are not in a tagged release yet. Do not merge before PHPStan 2.3.6 is tagged; then change the constraint to ^2.3.6.
  • CI stays red until upstream publishes a 2.3.x dev PHAR with #6958. Its "Compile PHAR" workflow currently fails on the Windows Turbo extension build, so it does not commit the PHAR.
  • The bump makes one @phpstan-ignore offsetAccess.notFound unmatched. phpstan analyse --fix removed it.
  • The expected fixtures use the .php.fixed extension. With .php, they collide with their inputs in the classmap and in detect-collisions.
  • When the fix removes all parameters, the printer writes fn() => instead of fn () =>.

Rules without a fix in this PR

  • enforceIteratorToArrayPreserveKeys: both true and false are valid. The rule exists to force that decision.
  • enforceReadonlyPublicProperty: readonly and private(set) are both valid, and readonly breaks writes outside the constructor.
  • enforceNativeReturnTypehint: possible follow-up. A new native return type on an overridable method breaks child classes without one. Fix rounds can fix those children in later rounds, but this needs its own PR.
  • forbidPhpDocNullabilityMismatchWithNativeTypehint: either side can be the wrong one.

Co-Authored-By: Claude Code

PHPStan 2.3.5 adds auto-fixable rules. These rules now attach a fix when only one fix is correct:

- forbidUnusedClosureParameters removes the trailing unused parameters.
- forbidMatchDefaultArmForEnums removes the default arm when the other arms list all cases of the native type. A PHPDoc-only narrowing does not count, because a removed arm then causes UnhandledMatchError at runtime.
- forbidUselessNullableReturn removes null from the native return type. It does not fix the error when the PHPDoc return type contains null, because the native type then becomes narrower than the PHPDoc type.
- uselessPrivatePropertyDefaultValue removes the default value.

PHPStan finds the node to fix by its class and file position. Two rules need other nodes than before:
- forbidMatchDefaultArmForEnums processes Match_, because MatchExpressionNode does not expose the MatchArm nodes.
- forbidUselessNullableReturn gets the return type node from the new ReturnTypeNodeVisitor. ReturnStatementsNode does not expose the ClassMethod or Function_ node.

The PHPStan bump also makes one @PHPStan-Ignore unmatched. `phpstan analyse --fix` removed it.

Co-Authored-By: Claude Code
phpstan/phpstan-src#6958 adds MethodReturnStatementsNode::getClassMethod() and FunctionReturnStatementsNode::getFunction(). forbidUselessNullableReturn now gets the native return type node from them, and ReturnTypeNodeVisitor is removed.

The getters are not in a tagged release yet, so composer.json requires ^2.3.6@dev. Change it to ^2.3.6 after the tag.

Co-Authored-By: Claude Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant