Skip to content

Add regression test for circular class constant PHPDoc type references#5685

Open
phpstan-bot wants to merge 3 commits into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-1ene83n
Open

Add regression test for circular class constant PHPDoc type references#5685
phpstan-bot wants to merge 3 commits into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-1ene83n

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

Issue #9172 reported that circular PHPDoc type annotations on class constants (e.g., @var int<0, self::MAX_DEPOSIT> on MIN_DEPOSIT and @var int<self::MIN_DEPOSIT, max> on MAX_DEPOSIT) caused PHPStan to hang indefinitely. The underlying hang was already fixed by the currentlyResolvingClassConstant cycle detection guard in InitializerExprTypeResolver (commit 93af41b), but no regression test existed for the specific scenario described in the issue.

Changes

  • Added tests/PHPStan/Analyser/nsrt/bug-9172.php — type assertion test verifying:
    • Final class constants with cross-referencing PHPDoc types resolve to their literal values
    • Abstract class constants with cross-referencing PHPDoc types resolve correctly via static:: access (int<0, 20000> and int<1000, max>)
    • Using constants as bounds in @param type annotations resolves correctly (int<1000, 20000>)
  • Added tests/PHPStan/Analyser/data/bug-9172.php — integration test data file with:
    • The exact code from the issue (non-circular values with cross-referencing PHPDoc types)
    • A circular value reference case (where both values AND PHPDoc types reference each other)
  • Added testBug9172() in tests/PHPStan/Analyser/AnalyserIntegrationTest.php verifying:
    • The code completes without hanging
    • The circular value case correctly reports "unresolvable type" errors

Root cause

The original hang occurred because InitializerExprTypeResolver::getClassConstFetchTypeByReflection() had no protection against circular class constant resolution. This was fixed in commit 93af41b by adding a $currentlyResolvingClassConstant tracking array that returns MixedType() when a cycle is detected.

For the specific case in issue #9172 (literal values with cross-referencing PHPDoc annotations), the resolution actually works without triggering the cycle guard because TypeNodeResolver::resolveConstTypeNode() resolves constants by reading their VALUE expressions (not their declared types), so self::MAX_DEPOSIT resolves to the literal 20000 and self::MIN_DEPOSIT resolves to 1000 — no recursion needed.

Test

  • tests/PHPStan/Analyser/nsrt/bug-9172.php — asserts correct type inference for the cross-referencing constant types
  • tests/PHPStan/Analyser/data/bug-9172.php with testBug9172() — integration test proving no hang occurs

Analogous cases probed and found to be already correct:

  • Global constants with circular references (handled by ConstantResolver::$currentlyResolving)
  • Type aliases with circular references (handled by UsefulTypeAliasResolver::$inProcess)
  • Self-referencing constants (handled by the same $currentlyResolvingClassConstant guard, tested in bug-10147)
  • Cross-class constant references with circular PHPDoc types (resolved via value expressions, no cycle)

Fixes phpstan/phpstan#9172

- Add nsrt test verifying correct type resolution when class constant
  PHPDoc types cross-reference each other (e.g. int<0, self::MAX> and
  int<self::MIN, max>)
- Add integration test proving the code completes without hanging,
  covering both the non-circular value case (literals) and the circular
  value case (which reports unresolvable type errors)
- The underlying hang was fixed by the currentlyResolvingClassConstant
  guard in InitializerExprTypeResolver (commit 93af41b), but no
  regression test existed for the specific scenario from issue #9172

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread tests/PHPStan/Analyser/AnalyserIntegrationTest.php Outdated
The test just needs to verify PHPStan doesn't hang on circular class
constant PHPDoc references — asserting specific error messages is
unnecessary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread tests/PHPStan/Analyser/nsrt/bug-9172.php Outdated
The integration test alone is sufficient to verify PHPStan doesn't
hang on circular class constant PHPDoc type references.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@VincentLanglet VincentLanglet requested a review from staabm May 16, 2026 20:23
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