Skip to content

Commit 19e4658

Browse files
Resolve static in assert PHPDoc
1 parent e3c0981 commit 19e4658

File tree

8 files changed

+211
-1
lines changed

8 files changed

+211
-1
lines changed

src/Reflection/Dummy/ChangedTypeMethodReflection.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public function __construct(
2727
private ?array $namedArgumentsVariants,
2828
private ?Type $selfOutType,
2929
private ?Type $throwType,
30+
private Assertions $assertions,
3031
)
3132
{
3233
}
@@ -133,7 +134,7 @@ public function hasSideEffects(): TrinaryLogic
133134

134135
public function getAsserts(): Assertions
135136
{
136-
return $this->reflection->getAsserts();
137+
return $this->assertions;
137138
}
138139

139140
public function acceptsNamedArguments(): TrinaryLogic

src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ private function transformMethodWithStaticType(ClassReflection $declaringClass,
137137
$namedArgumentVariants,
138138
$selfOutType,
139139
$throwType,
140+
$method->getAsserts()->mapTypes($this->transformStaticTypeCallback),
140141
);
141142
}
142143

src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ private function transformMethodWithStaticType(ClassReflection $declaringClass,
132132
$namedArgumentsVariants,
133133
$selfOutType,
134134
$throwType,
135+
$method->getAsserts()->mapTypes(fn (Type $type): Type => $this->transformStaticType($type)),
135136
);
136137
}
137138

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12376;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* workaround https://github.com/phpstan/phpstan-src/pull/3853
9+
*
10+
* @template T of object
11+
* @param class-string<T> $class
12+
* @return T
13+
*/
14+
function newNonFinalInstance(string $class): object
15+
{
16+
return new $class();
17+
}
18+
19+
class Base {}
20+
21+
class A extends Base
22+
{
23+
/**
24+
* @template T of object
25+
* @param T $object
26+
* @return (T is static ? T : static)
27+
*
28+
* @phpstan-assert static $object
29+
*/
30+
public static function assertInstanceOf(object $object)
31+
{
32+
if (!$object instanceof static) {
33+
throw new \Exception();
34+
}
35+
36+
return $object;
37+
}
38+
}
39+
40+
class B extends A {}
41+
class C extends Base {}
42+
43+
$o = newNonFinalInstance(\DateTime::class);
44+
$r = A::assertInstanceOf($o);
45+
assertType('*NEVER*', $o);
46+
assertType('Bug12376\A', $r);
47+
48+
$o = newNonFinalInstance(A::class);
49+
$r = A::assertInstanceOf($o);
50+
assertType('Bug12376\A', $o);
51+
assertType('Bug12376\A', $r);
52+
53+
$o = newNonFinalInstance(B::class);
54+
$r = A::assertInstanceOf($o);
55+
assertType('Bug12376\B', $o);
56+
assertType('Bug12376\B', $r);
57+
58+
$o = newNonFinalInstance(C::class);
59+
$r = A::assertInstanceOf($o);
60+
assertType('*NEVER*', $o);
61+
assertType('Bug12376\A', $r);
62+
63+
$o = newNonFinalInstance(A::class);
64+
$r = B::assertInstanceOf($o);
65+
assertType('Bug12376\B', $o);
66+
assertType('Bug12376\B', $r);

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3608,6 +3608,16 @@ public function testBug9141(): void
36083608
$this->analyse([__DIR__ . '/data/bug-9141.php'], []);
36093609
}
36103610

3611+
public function testBug12548(): void
3612+
{
3613+
$this->checkThisOnly = false;
3614+
$this->checkNullables = true;
3615+
$this->checkUnionTypes = true;
3616+
$this->checkExplicitMixed = true;
3617+
3618+
$this->analyse([__DIR__ . '/data/bug-12548.php'], []);
3619+
}
3620+
36113621
public function testBug3589(): void
36123622
{
36133623
$this->checkThisOnly = false;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12548;
4+
5+
class BaseSat extends \stdClass
6+
{
7+
/**
8+
* @template T of object
9+
*
10+
* @param T $object
11+
*
12+
* @phpstan-assert-if-true =static $object
13+
*/
14+
public static function assertInstanceOf(object $object): bool
15+
{
16+
return $object instanceof static;
17+
}
18+
19+
/**
20+
* @template T of object
21+
*
22+
* @param T $object
23+
*
24+
* @return (T is static ? T : static)
25+
*
26+
* @phpstan-assert static $object
27+
*/
28+
public static function assertInstanceOf2(object $object)
29+
{
30+
if (!$object instanceof static) {
31+
throw new \Error('Object is not an instance of static class');
32+
}
33+
34+
return $object;
35+
}
36+
}
37+
38+
class StdSat extends BaseSat
39+
{
40+
public function foo(): void {}
41+
42+
/**
43+
* @template T of object
44+
*
45+
* @param T $object
46+
*
47+
* @return (T is static ? T : static)
48+
*
49+
* @phpstan-assert static $object
50+
*/
51+
public static function assertInstanceOf3(object $object)
52+
{
53+
if (!$object instanceof static) {
54+
throw new \Error('Object is not an instance of static class');
55+
}
56+
57+
return $object;
58+
}
59+
}
60+
61+
class TestCase
62+
{
63+
private function createStdSat(): \stdClass
64+
{
65+
return new StdSat();
66+
}
67+
68+
public function testAssertInstanceOf(): void
69+
{
70+
$o = $this->createStdSat();
71+
$o->foo(); // @phpstan-ignore method.nonObject (EXPECTED)
72+
73+
$o = $this->createStdSat();
74+
if (StdSat::assertInstanceOf($o)) {
75+
$o->foo();
76+
}
77+
78+
$o = $this->createStdSat();
79+
StdSat::assertInstanceOf2($o);
80+
$o->foo();
81+
82+
$o = $this->createStdSat();
83+
StdSat::assertInstanceOf3($o);
84+
$o->foo();
85+
}
86+
}

tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,14 @@ public function testBug12645(): void
11651165
]);
11661166
}
11671167

1168+
public function testBug11289(): void
1169+
{
1170+
$this->checkThisOnly = false;
1171+
$this->checkUnionTypes = true;
1172+
$this->checkDynamicProperties = false;
1173+
$this->analyse([__DIR__ . '/data/bug-11289.php'], []);
1174+
}
1175+
11681176
public function testBug8668(): void
11691177
{
11701178
$this->checkThisOnly = false;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug11289;
6+
7+
abstract class SomeAbstractClass
8+
{
9+
private bool $someValue = true;
10+
11+
/**
12+
* @phpstan-assert-if-true =static $other
13+
*/
14+
public function equals(?self $other): bool
15+
{
16+
return $other instanceof static
17+
&& $this->someValue === $other->someValue;
18+
}
19+
}
20+
21+
class SomeConcreteClass extends SomeAbstractClass
22+
{
23+
public function __construct(
24+
private bool $someOtherValue,
25+
) {}
26+
27+
public function equals(?SomeAbstractClass $other): bool
28+
{
29+
return parent::equals($other)
30+
&& $this->someOtherValue === $other->someOtherValue;
31+
}
32+
}
33+
34+
$a = new SomeConcreteClass(true);
35+
$b = new SomeConcreteClass(false);
36+
37+
var_dump($a->equals($b), $b->equals($b));

0 commit comments

Comments
 (0)