Skip to content

Skip ClosureToArrowFunctionRector when closure has @var docblock#7885

Merged
TomasVotruba merged 1 commit intorectorphp:mainfrom
QDenka:fix/skip-closure-to-arrow-with-var-docblock
Feb 8, 2026
Merged

Skip ClosureToArrowFunctionRector when closure has @var docblock#7885
TomasVotruba merged 1 commit intorectorphp:mainfrom
QDenka:fix/skip-closure-to-arrow-with-var-docblock

Conversation

@QDenka
Copy link
Contributor

@QDenka QDenka commented Feb 8, 2026

Summary

Fixes rectorphp/rector#9637

Arrow functions do not support inline @var annotations for type narrowing. When a closure contains a @var docblock (e.g. /** @var Builder<Team> $query */), converting it to an arrow function breaks PHPStan type narrowing since there is no way to apply @var type annotations within arrow functions.

Problem

Previously, the rule only skipped conversion when the @var type was more specific than the native type (via type comparison). However, this comparison could fail with generics (e.g. Builder<Team> vs Builder), causing unintended conversions.

Example from the issue:

// Before (correct - PHPStan recognizes the type narrowing)
function (Builder $query) {
    /** @var Builder<Team> $query */
    return $query->inFullNameOrder();
}

// After (broken - @var has no effect in arrow functions)
/** @var Builder<Team> $query */
fn(Builder $query) => $query->inFullNameOrder()

Solution

Skip ClosureToArrowFunctionRector whenever a @var tag is present on the return statement, regardless of type comparison. This is the safe approach because:

  1. If the user placed a @var docblock, they did so intentionally
  2. Arrow functions cannot support @var annotations for type narrowing
  3. Type comparison with generics is unreliable

Changes

  • Simplified shouldSkipMoreSpecificTypeWithVarDoc() to skip on any @var presence
  • Removed unused NodeTypeResolver and MixedType dependencies
  • Added test fixture for generic type @var docblock scenario
  • Updated with_equal_var_doc_type fixture to be a skip test (equal types should also be preserved)

Arrow functions do not support inline @var annotations for type
narrowing. Previously, the rule only skipped conversion when the
@var type was more specific than the native type. However, type
comparison can fail with generics (e.g. Builder<Team> vs Builder),
causing unintended conversion that breaks PHPStan type narrowing.

Now the rule skips conversion whenever a @var tag is present on
the return statement, regardless of type comparison.

Fixes rectorphp/rector#9637
@TomasVotruba
Copy link
Member

Thanks for detailed analysis. I agree this reduction to arrow function would remove important metadata and context, that is clearly intended to keep.

@TomasVotruba TomasVotruba merged commit 4ed71ae into rectorphp:main Feb 8, 2026
59 checks passed
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.

ClosureToArrowFunctionRector Should not run if there is a phpstan @var comment in the fucntion

2 participants