Skip to content

Fix DocBlockVar fixer corrupting callable/Closure types#69

Merged
dereuromark merged 2 commits into
masterfrom
fix/docblockvar-callable-type
Jun 6, 2026
Merged

Fix DocBlockVar fixer corrupting callable/Closure types#69
dereuromark merged 2 commits into
masterfrom
fix/docblockvar-callable-type

Conversation

@dereuromark
Copy link
Copy Markdown
Contributor

@dereuromark dereuromark commented Jun 6, 2026

Problem

The DocBlockVar fixer corrupts callable/Closure property types when it appends a
missing null.

handle() splits the doc type on its first space to peel off a trailing comment,
assuming the type itself has no internal spaces. A callable/Closure signature has a
space after the colon, so the split cuts the type in half:

/**
 * @var \Closure(string): string
 */
protected ?\Closure $x = null;

phpcbf turns that into invalid output:

 * @var \Closure(string):|null string

PHPStan then rejects it as an unparseable doc type ("Unexpected token (").

Fix

Skip types whose syntax contains internal spaces/structure that the simple first-space
split cannot handle. Generics (<) and array shapes ({) are already skipped; this adds
callable/Closure signatures ((). The fixer then leaves such annotations untouched
instead of mangling them.

A regression fixture (a ?Closure property with a \Closure(string): string type) is
added to tests/_data/DocBlockVar; the fixer must leave it unchanged.

Found while it corrupted a real ?Closure property annotation in another project.

The var-annotation fixer splits the type on its first space to separate a trailing
comment, assuming types have no internal spaces. A callable/Closure signature such as
`\Closure(string): string` has a space after the colon, so the split cut the type in
half and the null-append produced invalid output (`\Closure(string):|null string`),
which PHPStan then rejects as an unparseable doc type.

Skip types containing `(` (callable/Closure signatures) the same way generics and
array shapes are already skipped, since the first-space split cannot handle their
internal structure. Adds a regression fixture.
Copilot AI review requested due to automatic review settings June 6, 2026 17:28
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prevents DocBlockVar’s fixer logic from corrupting PHPDoc @var types for callable/\Closure signatures by skipping annotations that contain internal structure the current “split on first space” parsing can’t safely handle.

Changes:

  • Add a regression fixture for a ?\Closure-typed property with @var \Closure(string): string.
  • Update DocBlockVarSniff::handle() to early-return for @var type strings containing ( (in addition to existing { / < skips).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
tests/_data/DocBlockVar/before.php Adds a fixture case involving a \Closure(string): string doc type.
tests/_data/DocBlockVar/after.php Expects the fixer to leave the new callable/\Closure doc type unchanged.
PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php Skips processing for doc types with ( to avoid corrupting callable/\Closure signatures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php Outdated
Split the doc type from its trailing description before the structural
check, then test only the type token. The previous check ran on the full
string, so a simple type with a parenthetical description (e.g.
`@var string (deprecated)`) was wrongly skipped and never got its missing
`null` appended. Closure/generic/array-shape types still skip correctly
because their opening token sits before the first space.

Also use strict `!== false` for the space split so an index-0 match is
not treated as "no space".

Add a regression fixture for the description-with-parentheses case.
@dereuromark dereuromark merged commit 78498f0 into master Jun 6, 2026
4 checks passed
@dereuromark dereuromark deleted the fix/docblockvar-callable-type branch June 6, 2026 17:41
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.

2 participants