Skip to content

Commit 82a6961

Browse files
authored
Merge branch refs/heads/1.12.x into 2.1.x
2 parents 7c281ba + a063119 commit 82a6961

File tree

3 files changed

+200
-2
lines changed

3 files changed

+200
-2
lines changed

src/Analyser/MutatingScope.php

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5631,13 +5631,77 @@ private function exactInstantiation(New_ $node, string $className): ?Type
56315631
}
56325632
}
56335633

5634-
if ($constructorMethod instanceof DummyConstructorReflection || $constructorMethod->getDeclaringClass()->getName() !== $classReflection->getName()) {
5634+
if ($constructorMethod instanceof DummyConstructorReflection) {
56355635
return new GenericObjectType(
56365636
$resolvedClassName,
56375637
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
56385638
);
56395639
}
56405640

5641+
if ($constructorMethod->getDeclaringClass()->getName() !== $classReflection->getName()) {
5642+
if (!$constructorMethod->getDeclaringClass()->isGeneric()) {
5643+
return new GenericObjectType(
5644+
$resolvedClassName,
5645+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
5646+
);
5647+
}
5648+
$newType = new GenericObjectType($resolvedClassName, $classReflection->typeMapToList($classReflection->getTemplateTypeMap()));
5649+
$ancestorType = $newType->getAncestorWithClassName($constructorMethod->getDeclaringClass()->getName());
5650+
if ($ancestorType === null) {
5651+
return new GenericObjectType(
5652+
$resolvedClassName,
5653+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
5654+
);
5655+
}
5656+
$ancestorClassReflections = $ancestorType->getObjectClassReflections();
5657+
if (count($ancestorClassReflections) !== 1) {
5658+
return new GenericObjectType(
5659+
$resolvedClassName,
5660+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
5661+
);
5662+
}
5663+
5664+
$newParentNode = new New_(new Name($constructorMethod->getDeclaringClass()->getName()), $node->args);
5665+
$newParentType = $this->getType($newParentNode);
5666+
$newParentTypeClassReflections = $newParentType->getObjectClassReflections();
5667+
if (count($newParentTypeClassReflections) !== 1) {
5668+
return new GenericObjectType(
5669+
$resolvedClassName,
5670+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
5671+
);
5672+
}
5673+
$newParentTypeClassReflection = $newParentTypeClassReflections[0];
5674+
5675+
$ancestorClassReflection = $ancestorClassReflections[0];
5676+
$ancestorMapping = [];
5677+
foreach ($ancestorClassReflection->getActiveTemplateTypeMap()->getTypes() as $typeName => $templateType) {
5678+
if (!$templateType instanceof TemplateType) {
5679+
continue;
5680+
}
5681+
5682+
$ancestorMapping[$typeName] = $templateType->getName();
5683+
}
5684+
5685+
$resolvedTypeMap = [];
5686+
foreach ($newParentTypeClassReflection->getActiveTemplateTypeMap()->getTypes() as $typeName => $type) {
5687+
if (!array_key_exists($typeName, $ancestorMapping)) {
5688+
continue;
5689+
}
5690+
5691+
if (!array_key_exists($ancestorMapping[$typeName], $resolvedTypeMap)) {
5692+
$resolvedTypeMap[$ancestorMapping[$typeName]] = $type;
5693+
continue;
5694+
}
5695+
5696+
$resolvedTypeMap[$ancestorMapping[$typeName]] = TypeCombinator::union($resolvedTypeMap[$ancestorMapping[$typeName]], $type);
5697+
}
5698+
5699+
return new GenericObjectType(
5700+
$resolvedClassName,
5701+
$classReflection->typeMapToList(new TemplateTypeMap($resolvedTypeMap)),
5702+
);
5703+
}
5704+
56415705
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
56425706
$this,
56435707
$methodCall->getArgs(),
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
3+
namespace Bug2735;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Dog {}
8+
9+
class Cat {}
10+
11+
/**
12+
* @template T
13+
*/
14+
class Collection {
15+
/** @var array<T> */
16+
protected $arr = [];
17+
18+
/**
19+
* @param array<T> $arr
20+
*/
21+
public function __construct(array $arr) {
22+
$this->arr = $arr;
23+
}
24+
25+
/**
26+
* @return T
27+
*/
28+
public function last()
29+
{
30+
if (!$this->arr) {
31+
throw new \Exception('bad');
32+
}
33+
return end($this->arr);
34+
}
35+
}
36+
37+
/**
38+
* @template T
39+
* @extends Collection<T>
40+
*/
41+
class CollectionChild extends Collection {
42+
}
43+
44+
$dogs = new CollectionChild([new Dog(), new Dog()]);
45+
assertType('Bug2735\\CollectionChild<Bug2735\\Dog>', $dogs);
46+
47+
/**
48+
* @template X
49+
* @template Y
50+
*/
51+
class ParentWithConstructor
52+
{
53+
54+
/**
55+
* @param X $x
56+
* @param Y $y
57+
*/
58+
public function __construct($x, $y)
59+
{
60+
}
61+
62+
}
63+
64+
/**
65+
* @template T
66+
* @extends ParentWithConstructor<int, T>
67+
*/
68+
class ChildOne extends ParentWithConstructor
69+
{
70+
71+
}
72+
73+
function (): void {
74+
$a = new ChildOne(1, new Dog());
75+
assertType('Bug2735\\ChildOne<Bug2735\\Dog>', $a);
76+
};
77+
78+
/**
79+
* @template T
80+
* @extends ParentWithConstructor<T, int>
81+
*/
82+
class ChildTwo extends ParentWithConstructor
83+
{
84+
85+
}
86+
87+
function (): void {
88+
$a = new ChildTwo(new Cat(), 2);
89+
assertType('Bug2735\\ChildTwo<Bug2735\\Cat>', $a);
90+
};
91+
92+
/**
93+
* @template T
94+
* @extends ParentWithConstructor<T, T>
95+
*/
96+
class ChildThree extends ParentWithConstructor
97+
{
98+
99+
}
100+
101+
function (): void {
102+
$a = new ChildThree(new Cat(), new Dog());
103+
assertType('Bug2735\\ChildThree<Bug2735\\Cat|Bug2735\\Dog>', $a);
104+
};
105+
106+
/**
107+
* @template T
108+
* @template U
109+
* @extends ParentWithConstructor<T, U>
110+
*/
111+
class ChildFour extends ParentWithConstructor
112+
{
113+
114+
}
115+
116+
function (): void {
117+
$a = new ChildFour(new Cat(), new Dog());
118+
assertType('Bug2735\\ChildFour<Bug2735\\Cat, Bug2735\\Dog>', $a);
119+
};
120+
121+
/**
122+
* @template T
123+
* @template U
124+
* @extends ParentWithConstructor<U, T>
125+
*/
126+
class ChildFive extends ParentWithConstructor
127+
{
128+
129+
}
130+
131+
function (): void {
132+
$a = new ChildFive(new Cat(), new Dog());
133+
assertType('Bug2735\\ChildFive<Bug2735\\Dog, Bug2735\\Cat>', $a);
134+
};

tests/PHPStan/Analyser/nsrt/generics.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ function testClasses()
741741
assertType('DateTime', $ab->getB(new \DateTime()));
742742

743743
$noConstructor = new NoConstructor(1);
744-
assertType('PHPStan\Generics\FunctionsAssertType\NoConstructor<mixed>', $noConstructor);
744+
assertType('PHPStan\Generics\FunctionsAssertType\NoConstructor<int>', $noConstructor);
745745

746746
assertType('stdClass', acceptsClassString(\stdClass::class));
747747
assertType('class-string<stdClass>', returnsClassString(new \stdClass()));

0 commit comments

Comments
 (0)