Skip to content

Commit c652f44

Browse files
Add non regression test
1 parent c7c7d7a commit c652f44

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,18 @@ public function testBug12457(): void
586586
]);
587587
}
588588

589+
public function testGenericSubtype(): void
590+
{
591+
$this->checkTypeAgainstPhpDocType = true;
592+
$this->strictWideningCheck = true;
593+
$this->analyse([__DIR__ . '/data/generic-subtype.php'], [
594+
[
595+
'PHPDoc tag @var with type GenericSubtype\IRepository<GenericSubtype\Foo> is not subtype of type GenericSubtype\IRepository<GenericSubtype\IEntity>.',
596+
131,
597+
],
598+
]);
599+
}
600+
589601
public function testNewIsAlwaysFinalClass(): void
590602
{
591603
$this->checkTypeAgainstPhpDocType = true;
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
namespace GenericSubtype;
4+
5+
interface IEntity {
6+
/**
7+
* @return IRepository<IEntity>
8+
*/
9+
public function getRepository(): IRepository;
10+
}
11+
12+
interface IProperty {}
13+
14+
interface IPropertyContainer extends IProperty {}
15+
16+
/**
17+
* @template E of IEntity
18+
*/
19+
interface IEntityAwareProperty extends IProperty {}
20+
21+
/**
22+
* @template E of IEntity
23+
* @extends IEntityAwareProperty<E>
24+
*/
25+
interface IRelationshipContainer extends IPropertyContainer, IEntityAwareProperty {}
26+
27+
interface IModel {
28+
/**
29+
* @template E of IEntity
30+
* @template T of IRepository<E>
31+
* @param class-string<T> $className
32+
* @return T
33+
*/
34+
public function getRepository(string $className): IRepository;
35+
}
36+
37+
/**
38+
* @template E of IEntity
39+
*/
40+
interface IRepository {
41+
public function getModel(): IModel;
42+
}
43+
44+
class PropertyRelationshipMetadata {
45+
/** @var class-string<IRepository<IEntity>> */
46+
public string $repository;
47+
}
48+
49+
/**
50+
* @template E of IEntity
51+
* @implements IRelationshipContainer<E>
52+
*/
53+
class HasOne implements IRelationshipContainer
54+
{
55+
/** @var E|null */
56+
protected ?IEntity $parent = null;
57+
58+
/** @var IRepository<E>|null */
59+
protected ?IRepository $targetRepository = null;
60+
61+
protected PropertyRelationshipMetadata $metadataRelationship;
62+
63+
/**
64+
* @return E
65+
*/
66+
protected function getParentEntity(): IEntity
67+
{
68+
return $this->parent ?? throw new \InvalidArgumentException('Relationship is not attached to a parent entity.');
69+
}
70+
71+
/**
72+
* @return IRepository<E>
73+
*/
74+
protected function getTargetRepository(): IRepository
75+
{
76+
if ($this->targetRepository === null) {
77+
/** @var IRepository<E> $targetRepository */
78+
$targetRepository = $this->getParentEntity()
79+
->getRepository()
80+
->getModel()
81+
->getRepository($this->metadataRelationship->repository);
82+
83+
$this->test($targetRepository);
84+
85+
$this->targetRepository = $targetRepository;
86+
}
87+
88+
return $this->targetRepository;
89+
}
90+
91+
/**
92+
* @param IRepository<IEntity>
93+
*/
94+
protected function test(): void {}
95+
}
96+
97+
class Foo implements IEntity {
98+
public function getRepository(): IRepository {
99+
throw new \BadMethodCallException();
100+
}
101+
}
102+
103+
/**
104+
* @implements IRelationshipContainer<Foo>
105+
*/
106+
class HasOne2 implements IRelationshipContainer
107+
{
108+
/** @var Foo|null */
109+
protected ?IEntity $parent = null;
110+
111+
/** @var IRepository<Foo>|null */
112+
protected ?IRepository $targetRepository = null;
113+
114+
protected PropertyRelationshipMetadata $metadataRelationship;
115+
116+
/**
117+
* @return Foo
118+
*/
119+
protected function getParentEntity(): IEntity
120+
{
121+
return $this->parent ?? throw new \InvalidArgumentException('Relationship is not attached to a parent entity.');
122+
}
123+
124+
/**
125+
* @return IRepository<Foo>
126+
*/
127+
protected function getTargetRepository(): IRepository
128+
{
129+
if ($this->targetRepository === null) {
130+
/** @var IRepository<Foo> $targetRepository */
131+
$targetRepository = $this->getParentEntity()
132+
->getRepository()
133+
->getModel()
134+
->getRepository($this->metadataRelationship->repository);
135+
136+
$this->test($targetRepository);
137+
138+
$this->targetRepository = $targetRepository;
139+
}
140+
141+
return $this->targetRepository;
142+
}
143+
144+
/**
145+
* @param IRepository<IEntity> $repository
146+
*/
147+
protected function test($repository): void {}
148+
}

0 commit comments

Comments
 (0)