Skip to content

Commit f77d18f

Browse files
herndlmondrejmirtes
authored andcommitted
Allow objects in instanceof-like assertions
1 parent ae57b8f commit f77d18f

File tree

5 files changed

+97
-11
lines changed

5 files changed

+97
-11
lines changed

src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php

+19-6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
use PHPStan\Type\Type;
4141
use PHPStan\Type\TypeCombinator;
4242
use PHPStan\Type\TypeUtils;
43+
use PHPStan\Type\TypeWithClassName;
4344
use ReflectionObject;
4445
use Traversable;
4546
use function array_key_exists;
@@ -335,25 +336,33 @@ private static function getExpressionResolvers(): array
335336
},
336337
'isInstanceOf' => static function (Scope $scope, Arg $expr, Arg $class): ?Expr {
337338
$classType = $scope->getType($class->value);
338-
if (!$classType instanceof ConstantStringType) {
339+
if ($classType instanceof ConstantStringType) {
340+
$className = new Name($classType->getValue());
341+
} elseif ($classType instanceof TypeWithClassName) {
342+
$className = new Name($classType->getClassName());
343+
} else {
339344
return null;
340345
}
341346

342347
return new Instanceof_(
343348
$expr->value,
344-
new Name($classType->getValue())
349+
$className
345350
);
346351
},
347352
'notInstanceOf' => static function (Scope $scope, Arg $expr, Arg $class): ?Expr {
348353
$classType = $scope->getType($class->value);
349-
if (!$classType instanceof ConstantStringType) {
354+
if ($classType instanceof ConstantStringType) {
355+
$className = new Name($classType->getValue());
356+
} elseif ($classType instanceof TypeWithClassName) {
357+
$className = new Name($classType->getClassName());
358+
} else {
350359
return null;
351360
}
352361

353362
return new BooleanNot(
354363
new Instanceof_(
355364
$expr->value,
356-
new Name($classType->getValue())
365+
$className
357366
)
358367
);
359368
},
@@ -639,11 +648,15 @@ static function (Type $type): Type {
639648

640649
if ($methodName === 'allNotInstanceOf') {
641650
$classType = $scope->getType($node->getArgs()[1]->value);
642-
if (!$classType instanceof ConstantStringType) {
651+
652+
if ($classType instanceof ConstantStringType) {
653+
$objectType = new ObjectType($classType->getValue());
654+
} elseif ($classType instanceof TypeWithClassName) {
655+
$objectType = new ObjectType($classType->getClassName());
656+
} else {
643657
return new SpecifiedTypes([], []);
644658
}
645659

646-
$objectType = new ObjectType($classType->getValue());
647660
return $this->arrayOrIterable(
648661
$scope,
649662
$node->getArgs()[0]->value,

tests/Type/WebMozartAssert/ImpossibleCheckTypeMethodCallRuleTest.php

+20
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@ public function testExtension(): void
2424
'Call to static method Webmozart\Assert\Assert::stringNotEmpty() with \'\' will always evaluate to false.',
2525
13,
2626
],
27+
[
28+
'Call to static method Webmozart\Assert\Assert::isInstanceOf() with WebmozartAssertImpossibleCheck\Bar and \'WebmozartAssertImpossibleCheck\\\Bar\' will always evaluate to true.',
29+
21,
30+
],
31+
[
32+
'Call to static method Webmozart\Assert\Assert::isInstanceOf() with WebmozartAssertImpossibleCheck\Bar and WebmozartAssertImpossibleCheck\Bar will always evaluate to true.',
33+
22,
34+
],
35+
[
36+
'Call to static method Webmozart\Assert\Assert::nullOrIsInstanceOf() with WebmozartAssertImpossibleCheck\Bar and \'WebmozartAssertImpossibleCheck\\\Bar\' will always evaluate to true.',
37+
23,
38+
],
39+
[
40+
'Call to static method Webmozart\Assert\Assert::allIsInstanceOf() with array<WebmozartAssertImpossibleCheck\Bar> and \'WebmozartAssertImpossibleCheck\\\Bar\' will always evaluate to true.',
41+
26,
42+
],
43+
[
44+
'Call to static method Webmozart\Assert\Assert::notInstanceOf() with WebmozartAssertImpossibleCheck\Bar and \'WebmozartAssertImpossibleCheck\\\Bar\' will always evaluate to false.',
45+
32,
46+
],
2747
]);
2848
}
2949

tests/Type/WebMozartAssert/data/collection.php

+18-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type\WebMozartAssert;
44

5+
use stdClass;
56
use Webmozart\Assert\Assert;
67

78
class CollectionTest
@@ -28,19 +29,33 @@ public function allInteger(array $a, iterable $b, iterable $c): void
2829
\PHPStan\Testing\assertType('iterable<int>', $b);
2930
}
3031

31-
public function allInstanceOf(array $a): void
32+
public function allInstanceOf(array $a, array $b, array $c): void
3233
{
33-
Assert::allIsInstanceOf($a, \stdClass::class);
34+
Assert::allIsInstanceOf($a, stdClass::class);
3435
\PHPStan\Testing\assertType('array<stdClass>', $a);
36+
37+
Assert::allIsInstanceOf($b, new stdClass());
38+
\PHPStan\Testing\assertType('array<stdClass>', $b);
39+
40+
Assert::allIsInstanceOf($c, 17);
41+
\PHPStan\Testing\assertType('array', $c);
3542
}
3643

3744
/**
3845
* @param (CollectionFoo|CollectionBar)[] $a
46+
* @param (CollectionFoo|stdClass)[] $b
47+
* @param CollectionFoo[] $c
3948
*/
40-
public function allNotInstanceOf(array $a): void
49+
public function allNotInstanceOf(array $a, array $b, array $c): void
4150
{
4251
Assert::allNotInstanceOf($a, CollectionBar::class);
4352
\PHPStan\Testing\assertType('array<PHPStan\Type\WebMozartAssert\CollectionFoo>', $a);
53+
54+
Assert::allNotInstanceOf($b, new stdClass());
55+
\PHPStan\Testing\assertType('array<PHPStan\Type\WebMozartAssert\CollectionFoo>', $b);
56+
57+
Assert::allNotInstanceOf($c, 17);
58+
\PHPStan\Testing\assertType('array<PHPStan\Type\WebMozartAssert\CollectionFoo>', $c);
4459
}
4560

4661
/**

tests/Type/WebMozartAssert/data/impossible-check.php

+23
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,27 @@ public function doFoo(string $s): void
1313
Assert::stringNotEmpty('');
1414
}
1515

16+
/**
17+
* @param mixed[] $b
18+
*/
19+
public function isInstanceOf(Bar $a, array $b): void
20+
{
21+
Assert::isInstanceOf($a, Bar::class);
22+
Assert::isInstanceOf($a, $a);
23+
Assert::nullOrIsInstanceOf($a, Bar::class);
24+
25+
Assert::allIsInstanceOf($b, Bar::class);
26+
Assert::allIsInstanceOf($b, Bar::class);
27+
}
28+
29+
public function notInstanceOf(Bar $a): void
30+
{
31+
Assert::notInstanceOf($a, Baz::class);
32+
Assert::notInstanceOf($a, Bar::class);
33+
}
34+
1635
}
36+
37+
interface Bar {};
38+
39+
class Baz {}

tests/Type/WebMozartAssert/data/type.php

+17-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type\WebMozartAssert;
44

5+
use stdClass;
56
use Webmozart\Assert\Assert;
67

78
class TypeTest
@@ -158,22 +159,36 @@ public function isCountable($a, $b): void
158159
\PHPStan\Testing\assertType('array|Countable|null', $b);
159160
}
160161

161-
public function isInstanceOf($a, $b): void
162+
public function isInstanceOf($a, $b, $c, $d): void
162163
{
163164
Assert::isInstanceOf($a, self::class);
164165
\PHPStan\Testing\assertType('PHPStan\Type\WebMozartAssert\TypeTest', $a);
165166

166167
Assert::nullOrIsInstanceOf($b, self::class);
167168
\PHPStan\Testing\assertType('PHPStan\Type\WebMozartAssert\TypeTest|null', $b);
169+
170+
Assert::isInstanceOf($c, new stdClass());
171+
\PHPStan\Testing\assertType('stdClass', $c);
172+
173+
Assert::isInstanceOf($d, 17);
174+
\PHPStan\Testing\assertType('mixed', $d);
168175
}
169176

170177
/**
171178
* @param Foo|Bar $a
179+
* @param Foo|stdClass $b
180+
* @param Foo|Bar $c
172181
*/
173-
public function notInstanceOf($a): void
182+
public function notInstanceOf($a, $b, $c): void
174183
{
175184
Assert::notInstanceOf($a, Bar::class);
176185
\PHPStan\Testing\assertType('PHPStan\Type\WebMozartAssert\Foo', $a);
186+
187+
Assert::notInstanceOf($b, new stdClass());
188+
\PHPStan\Testing\assertType('PHPStan\Type\WebMozartAssert\Foo', $b);
189+
190+
Assert::notInstanceOf($c, 17);
191+
\PHPStan\Testing\assertType('PHPStan\Type\WebMozartAssert\Bar|PHPStan\Type\WebMozartAssert\Foo', $c);
177192
}
178193

179194
public function isArrayAccessible($a, $b): void

0 commit comments

Comments
 (0)