Skip to content

Commit 73abb5e

Browse files
phpstan-botclaude
andcommitted
Add test for identical description disambiguation with invariant template 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>
1 parent 53a3230 commit 73abb5e

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,4 +423,16 @@ public function testBug13453(): void
423423
]);
424424
}
425425

426+
public function testBug13453Invariant(): void
427+
{
428+
$this->checkNullables = true;
429+
$this->checkExplicitMixed = true;
430+
$this->analyse([__DIR__ . '/data/bug-13453-invariant.php'], [
431+
[
432+
'Function Bug13453Invariant\run() should return Bug13453Invariant\Container<T of Bug13453Invariant\ResultA (function Bug13453Invariant\run(), argument)> but returns Bug13453Invariant\Container<T of Bug13453Invariant\ResultA (class Bug13453Invariant\I, parameter)>.',
433+
37,
434+
],
435+
]);
436+
}
437+
426438
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php // lint >= 8.3
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug13453Invariant;
6+
7+
/** @template T */
8+
class Container {
9+
/** @param T $value */
10+
public function __construct(public mixed $value) {}
11+
}
12+
13+
class ResultA {
14+
public function __construct(public string $value) {}
15+
}
16+
17+
class ResultB extends ResultA {}
18+
19+
/** @template T of ResultA */
20+
interface I {
21+
/** @var class-string<T> */
22+
public const string ResultType = ResultA::class;
23+
}
24+
25+
/** @template-implements I<ResultB> */
26+
class In implements I {
27+
public const string ResultType = ResultB::class;
28+
}
29+
30+
/**
31+
* @template T of ResultA
32+
* @param I<T> $in
33+
* @return Container<T>
34+
*/
35+
function run(I $in): Container {
36+
$value = 'abc';
37+
return new Container(new ($in::ResultType)($value));
38+
}

0 commit comments

Comments
 (0)