Skip to content

Commit d06f792

Browse files
committed
Inferring new from parent constructor - reject types that would fail bound check of the child class
1 parent dce063a commit d06f792

File tree

2 files changed

+77
-4
lines changed

2 files changed

+77
-4
lines changed

Diff for: src/Analyser/MutatingScope.php

+9-4
Original file line numberDiff line numberDiff line change
@@ -5575,7 +5575,7 @@ private function exactInstantiation(New_ $node, string $className): ?Type
55755575
continue;
55765576
}
55775577

5578-
$ancestorMapping[$typeName] = $templateType->getName();
5578+
$ancestorMapping[$typeName] = $templateType;
55795579
}
55805580

55815581
$resolvedTypeMap = [];
@@ -5584,12 +5584,17 @@ private function exactInstantiation(New_ $node, string $className): ?Type
55845584
continue;
55855585
}
55865586

5587-
if (!array_key_exists($ancestorMapping[$typeName], $resolvedTypeMap)) {
5588-
$resolvedTypeMap[$ancestorMapping[$typeName]] = $type;
5587+
$ancestorType = $ancestorMapping[$typeName];
5588+
if (!$ancestorType->getBound()->isSuperTypeOf($type)->yes()) {
55895589
continue;
55905590
}
55915591

5592-
$resolvedTypeMap[$ancestorMapping[$typeName]] = TypeCombinator::union($resolvedTypeMap[$ancestorMapping[$typeName]], $type);
5592+
if (!array_key_exists($ancestorType->getName(), $resolvedTypeMap)) {
5593+
$resolvedTypeMap[$ancestorType->getName()] = $type;
5594+
continue;
5595+
}
5596+
5597+
$resolvedTypeMap[$ancestorType->getName()] = TypeCombinator::union($resolvedTypeMap[$ancestorType->getName()], $type);
55935598
}
55945599

55955600
return new GenericObjectType(

Diff for: tests/PHPStan/Analyser/nsrt/bug-12386.php

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace Bug12386;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function doFoo() {
8+
$landMapper = new Application_Model_Mapper_Land();
9+
assertType('Bug12386\\Clx_Model_Iterator<Bug12386\\Application_Model_Land>', $landMapper->fetchAllActivePrependDefault(12));
10+
}
11+
12+
/**
13+
* @template T of Clx_Model_Abstract
14+
*/
15+
abstract class Clx_Model_Mapper_Abstract
16+
{
17+
public function __construct()
18+
{
19+
}
20+
}
21+
22+
/**
23+
* @template T of Application_Model_Land
24+
*
25+
* @extends Clx_Model_Mapper_Abstract<T>
26+
*/
27+
class ClxProductNet_Model_Mapper_Land extends Clx_Model_Mapper_Abstract
28+
{
29+
/**
30+
* @param int $defaultLandid
31+
*
32+
* @return Clx_Model_Iterator<T>
33+
*/
34+
public function fetchAllActivePrependDefault($defaultLandid): Clx_Model_Iterator
35+
{}
36+
}
37+
38+
/**
39+
* @template T of Application_Model_Land
40+
*
41+
* @extends ClxProductNet_Model_Mapper_Land<T>
42+
*/
43+
final class Application_Model_Mapper_Land extends ClxProductNet_Model_Mapper_Land
44+
{
45+
}
46+
47+
/**
48+
* @template T of Clx_Model_Abstract
49+
*
50+
* @implements \Iterator<T>
51+
*/
52+
abstract class Clx_Model_Iterator implements \Countable, \Iterator
53+
{}
54+
55+
abstract class Clx_Model_Abstract implements \Stringable
56+
{}
57+
58+
abstract class ClxProductNet_Model_Land extends Clx_Model_Abstract
59+
{}
60+
61+
final class Application_Model_Land extends ClxProductNet_Model_Land
62+
{
63+
public function __toString()
64+
{
65+
return 'foo';
66+
}
67+
68+
}

0 commit comments

Comments
 (0)