Skip to content

Fix phpstan/phpstan#13453: Templated return type error message is self-contradictory#5352

Open
phpstan-bot wants to merge 3 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-epcrhal
Open

Fix phpstan/phpstan#13453: Templated return type error message is self-contradictory#5352
phpstan-bot wants to merge 3 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-epcrhal

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When two different template types have the same name and bound but belong to different scopes (e.g., a function template T of ResultA and an interface template T of ResultA), error messages like "should return T of ResultA but returns T of ResultA" were self-contradictory and confusing to users. The fix escalates the verbosity level to precise when both types produce identical descriptions, adding scope information to disambiguate them.

Changes

  • Modified VerbosityLevel::getRecommendedLevelByType() in src/Type/VerbosityLevel.php to detect when accepting and accepted types have identical descriptions at the chosen level, and escalate to precise verbosity in that case
  • Added regression test in tests/PHPStan/Rules/Functions/data/bug-13453.php and corresponding test method in tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php

Root cause

VerbosityLevel::getRecommendedLevelByType() chose verbosity based on type complexity (constant values, callables, accessory types, etc.) but never considered whether the two types being compared would produce identical string descriptions. Template types from different scopes with the same name and bound are semantically different but described identically at typeOnly and value verbosity levels. Only at precise level does the scope information (e.g., "function run(), argument" vs "class I, parameter") get included.

Test

The regression test reproduces the exact scenario from the issue: a function with @template T of ResultA returning a value whose type is T of ResultA from an interface template. The test verifies the error message now includes scope disambiguation.

Fixes phpstan/phpstan#13453

…iptions in error messages

- When two different template types produce the same description at the chosen verbosity level, escalate to precise verbosity to include scope information
- This fixes self-contradictory messages like "should return T of ResultA but returns T of ResultA" by showing "T of ResultA (function run(), argument) but returns T of ResultA (class I, parameter)"
- New regression test in tests/PHPStan/Rules/Functions/data/bug-13453.php
Copy link
Copy Markdown
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

You made two changes, but only one test ; could you test the other change too ?

…late types

Cover the second code path in VerbosityLevel::getRecommendedLevelByType()
where containsInvariantTemplateType is true. The test uses a generic
Container<T> (invariant template) wrapping template types from different
scopes that would otherwise produce identical descriptions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@phpstan-bot
Copy link
Copy Markdown
Collaborator Author

Done. I added a test (testBug13453Invariant) that covers the second code path in VerbosityLevel::getRecommendedLevelByType() — the branch where $containsInvariantTemplateType is true. The test uses a generic Container<T> (which has an invariant template by default) wrapping template types from different scopes (function run() vs class I), verifying that the descriptions are disambiguated with scope information when they would otherwise be identical.

@VincentLanglet VincentLanglet self-assigned this Apr 4, 2026
Copy link
Copy Markdown
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

the written tests need Requires PHP >= 8.0

The test data files use PHP 8.3 typed constants (public const string),
so the test methods need the RequiresPhp attribute to skip on older PHP.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@phpstan-bot
Copy link
Copy Markdown
Collaborator Author

make phpstan also passed successfully — no static analysis issues.

Copy link
Copy Markdown
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

This fix seems ok (the right one ?) to me, but I'm unsure ondrej agree with the fact some error message will be updated.
On the other side, I don't see any other way to close this issue. Unless we fix other generic issues first...

WDYT @staabm ? Might be worth asking ondrej before merging this (?)

@VincentLanglet VincentLanglet requested a review from staabm April 5, 2026 21:16
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