Skip to content

Commit 0bff5cd

Browse files
committed
1 parent d8ec11f commit 0bff5cd

9 files changed

Lines changed: 262 additions & 0 deletions

File tree

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,15 @@ private static function findTestFiles(): iterable
206206
if (PHP_VERSION_ID >= 80100) {
207207
yield __DIR__ . '/../Rules/Comparison/data/bug-8485.php';
208208
yield __DIR__ . '/../Rules/PhpDoc/data/bug-11033.php';
209+
yield __DIR__ . '/../Rules/PhpDoc/data/bug-9708.php';
209210
}
210211

211212
if (PHP_VERSION_ID >= 80100) {
212213
yield __DIR__ . '/../Rules/Comparison/data/bug-9007.php';
213214
}
214215

216+
yield __DIR__ . '/../Rules/Generics/data/bug-9981.php';
217+
215218
yield __DIR__ . '/../Rules/DeadCode/data/bug-8620.php';
216219

217220
if (PHP_VERSION_ID >= 80200) {

tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,4 +285,9 @@ public function testBug11552(): void
285285
]);
286286
}
287287

288+
public function testBug7021(): void
289+
{
290+
$this->analyse([__DIR__ . '/data/bug-7021.php'], []);
291+
}
292+
288293
}

tests/PHPStan/Rules/Generics/InterfaceAncestorsRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,9 @@ public function testCrossCheckInterfaces(): void
213213
]);
214214
}
215215

216+
public function testBug9981(): void
217+
{
218+
$this->analyse([__DIR__ . '/data/bug-9981.php'], []);
219+
}
220+
216221
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bug7021;
4+
5+
interface DataObjectInterface {}
6+
7+
interface RepositoryInterface {}
8+
9+
interface QueryBuilderInterface {}
10+
11+
/**
12+
* @template DO of DataObjectInterface
13+
* @template RQB of RepositoryQueryBuilderInterface
14+
*/
15+
interface CachingReadOnlyQueryBuilderRepositoryInterface {}
16+
17+
/**
18+
* @template DO of DataObjectInterface
19+
*/
20+
interface RepositoryQueryBuilderInterface {}
21+
22+
/**
23+
* @template DO of DataObjectInterface
24+
* @template RQB of RepositoryQueryBuilderInterface
25+
*/
26+
interface ReadOnlyQueryBuilderRepositoryInterface extends RepositoryInterface {}
27+
28+
/**
29+
* @template DO of DataObjectInterface
30+
* @template R of RepositoryInterface
31+
*/
32+
class CachingReadOnlyRepositoryDecorator {}
33+
34+
/**
35+
* @template DO of DataObjectInterface
36+
* @template QB of QueryBuilderInterface
37+
* @template RQB of RepositoryQueryBuilderInterface<DO>
38+
* @template R of ReadOnlyQueryBuilderRepositoryInterface<DO, RQB>
39+
* @extends CachingReadOnlyRepositoryDecorator<DO, R>
40+
* @implements CachingReadOnlyQueryBuilderRepositoryInterface<DO, RQB>
41+
*/
42+
class CachingReadOnlyQueryBuilderRepositoryDecorator extends CachingReadOnlyRepositoryDecorator implements
43+
CachingReadOnlyQueryBuilderRepositoryInterface
44+
{
45+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9981;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
interface File {
8+
}
9+
10+
/**
11+
* @template TKey of array-key
12+
* @template T of File|Directory
13+
* @extends \Traversable<TKey,T>
14+
*/
15+
interface Directory extends \Traversable {
16+
}
17+
18+
class Foo
19+
{
20+
21+
/**
22+
* @param Directory<int, File> $d
23+
* @return void
24+
*/
25+
public function doFoo(Directory $d): void
26+
{
27+
foreach ($d as $k => $v) {
28+
assertType('int', $k);
29+
assertType(File::class, $v);
30+
}
31+
}
32+
33+
/**
34+
* @param Directory<int, Directory> $d
35+
* @return void
36+
*/
37+
public function doBar(Directory $d): void
38+
{
39+
foreach ($d as $k => $v) {
40+
assertType('int', $k);
41+
assertType(Directory::class, $v);
42+
}
43+
}
44+
45+
/**
46+
* @param Directory<int, File|Directory> $d
47+
* @return void
48+
*/
49+
public function doBaz(Directory $d): void
50+
{
51+
foreach ($d as $k => $v) {
52+
assertType('int', $k);
53+
assertType('Bug9981\\Directory|Bug9981\\File', $v);
54+
}
55+
}
56+
57+
}

tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,4 +1276,9 @@ public function testBug9494(): void
12761276
$this->analyse([__DIR__ . '/data/bug-9494.php'], []);
12771277
}
12781278

1279+
public function testBug11160(): void
1280+
{
1281+
$this->analyse([__DIR__ . '/data/bug-11160.php'], []);
1282+
}
1283+
12791284
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Bug11160;
6+
7+
/**
8+
* @template T
9+
* @phpstan-type MyCustomType = string
10+
*/
11+
trait MyTrait
12+
{
13+
/**
14+
* @return MyCustomType
15+
*/
16+
public function foo()
17+
{
18+
return 'hi';
19+
}
20+
}
21+
22+
class MyParent
23+
{
24+
/**
25+
* @use MyTrait<string>
26+
*/
27+
use MyTrait;
28+
}
29+
30+
/**
31+
* @phpstan-import-type MyCustomType from MyTrait
32+
*/
33+
class MyChild
34+
{
35+
36+
/**
37+
* @return MyCustomType
38+
*/
39+
function bar()
40+
{
41+
return $this->foo();
42+
}
43+
}

tests/PHPStan/Rules/PhpDoc/IncompatiblePhpDocTypeRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,4 +482,9 @@ public function testBug13652(): void
482482
]);
483483
}
484484

485+
public function testBug9708(): void
486+
{
487+
$this->analyse([__DIR__ . '/data/bug-9708.php'], []);
488+
}
489+
485490
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php // lint >= 8.1
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug9708;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
interface EventTopic {
10+
11+
}
12+
13+
enum FooEventTopic: string implements EventTopic {
14+
case Foo = 'foo';
15+
}
16+
17+
/**
18+
* @template TEventTopic of EventTopic
19+
*/
20+
interface EventType {
21+
22+
}
23+
24+
25+
/**
26+
* @template-implements EventType<FooEventTopic>
27+
*/
28+
enum FooEventType: string implements EventType {
29+
case Foo = 'foo';
30+
}
31+
32+
/**
33+
* @template TEventTopic of EventTopic
34+
* @template TEventType of EventType<TEventTopic>
35+
*/
36+
interface Event {
37+
38+
}
39+
40+
/**
41+
* @template-implements Event<FooEventTopic, FooEventType>
42+
*/
43+
abstract class FooEvent implements Event {
44+
45+
}
46+
47+
/**
48+
* This works as expected and is sufficient for this use-case
49+
*
50+
* @template TEvent of Event<EventTopic, EventType<EventTopic>>
51+
*/
52+
class EventDispatcher {
53+
54+
/**
55+
* @param TEvent $bar
56+
*/
57+
public function dispatch (Event $bar): void {
58+
assertType('TEvent of Bug9708\Event<Bug9708\EventTopic, Bug9708\EventType<Bug9708\EventTopic>> (class Bug9708\EventDispatcher, argument)', $bar);
59+
}
60+
}
61+
62+
/**
63+
* The generic parameter must be specified twice! Even though it's part of the template declaration
64+
*
65+
* @template TEventTopic of EventTopic
66+
* @template TEventType of EventType<TEventTopic>
67+
* @template TEvent of Event<TEventTopic, TEventType<TEventTopic>>
68+
*/
69+
class EventDispatcher2 {
70+
71+
/**
72+
* @param TEvent $bar
73+
*/
74+
public function dispatch (Event $bar): void {
75+
assertType('TEvent of Bug9708\Event<TEventTopic of Bug9708\EventTopic (class Bug9708\EventDispatcher2, argument), Bug9708\EventType<TEventTopic of Bug9708\EventTopic (class Bug9708\EventDispatcher2, argument)>> (class Bug9708\EventDispatcher2, argument)', $bar);
76+
}
77+
}
78+
79+
/**
80+
* This should work but doesn't
81+
*
82+
* @template TEventTopic of EventTopic
83+
* @template TEventType of EventType<TEventTopic>
84+
* @template TEvent of Event<TEventTopic, TEventType>
85+
*/
86+
class EventDispatcher3 {
87+
88+
/**
89+
* @param TEvent $bar
90+
*/
91+
public function dispatch (Event $bar): void {
92+
assertType('TEvent of Bug9708\Event<TEventTopic of Bug9708\EventTopic (class Bug9708\EventDispatcher3, argument), TEventType of Bug9708\EventType<TEventTopic of Bug9708\EventTopic (class Bug9708\EventDispatcher3, argument)> (class Bug9708\EventDispatcher3, argument)> (class Bug9708\EventDispatcher3, argument)', $bar);
93+
}
94+
}

0 commit comments

Comments
 (0)