Skip to content

Add FQCN support to implementsInterface #144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/Type/WebMozartAssert/AssertTypeSpecifyingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -451,10 +451,16 @@ private static function getExpressionResolvers(): array
return null;
}

return new Instanceof_(
$expr->value,
new Name($classType->getValue())
);
$classReflection = (new ObjectType($classType->getValue()))->getClassReflection();
if ($classReflection === null) {
return null;
}

if (!$classReflection->isInterface()) {
return new ConstFetch(new Name('false'));
}

return self::$resolvers['subclassOf']($scope, $expr, $class);
},
'keyExists' => static function (Scope $scope, Arg $array, Arg $key): Expr {
return new FuncCall(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/object.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/string.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/type.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-18.php');
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ public function testExtension(): void
'Call to static method Webmozart\Assert\Assert::allContains() with array<non-empty-string> and \'foo\' will always evaluate to true.',
98,
],
[
'Call to static method Webmozart\Assert\Assert::implementsInterface() with class-string<WebmozartAssertImpossibleCheck\Bar>|WebmozartAssertImpossibleCheck\Bar and \'WebmozartAssertImpossibleCheck\\\Bar\' will always evaluate to true.',
105,
],
[
'Call to static method Webmozart\Assert\Assert::implementsInterface() with class-string<WebmozartAssertImpossibleCheck\Bar> and \'WebmozartAssertImpossibleCheck\\\Bar\' will always evaluate to true.',
108,
],
[
'Call to static method Webmozart\Assert\Assert::implementsInterface() with mixed and \'WebmozartAssertImpossibleCheck\\\Foo\' will always evaluate to false.',
111,
],
]);
}

Expand Down Expand Up @@ -175,6 +187,21 @@ public function testBug8(): void
]);
}

public function testBug17(): void
{
$this->analyse([__DIR__ . '/data/bug-17.php'], [
[
'Call to static method Webmozart\Assert\Assert::implementsInterface() with \'DateTime\' and \'DateTimeInterface\' will always evaluate to true.',
9,
],
]);
}

public function testBug18(): void
{
$this->analyse([__DIR__ . '/data/bug-18.php'], []);
}

public function testBug32(): void
{
$this->analyse([__DIR__ . '/data/bug-32.php'], []);
Expand Down
11 changes: 11 additions & 0 deletions tests/Type/WebMozartAssert/data/bug-17.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare(strict_types=1);

namespace Bug17;

use DateTimeInterface;
use Webmozart\Assert\Assert;

(function () {
Assert::implementsInterface(\DateTime::class, DateTimeInterface::class);
Assert::implementsInterface(\DateTimeZone::class, DateTimeInterface::class);
})();
18 changes: 18 additions & 0 deletions tests/Type/WebMozartAssert/data/bug-18.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types=1);

namespace Bug18;

use Webmozart\Assert\Assert;
use function PHPStan\Testing\assertType;

class MyThingFactory
{
public function make(string $thing)
{
Assert::implementsInterface($thing, SomeDto::class);

assertType('class-string<Bug18\SomeDto>', $thing);
}
}

interface SomeDto {}
12 changes: 12 additions & 0 deletions tests/Type/WebMozartAssert/data/impossible-check.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ public function nonEmptyStringAndSomethingUnknownNarrow($a, string $b, array $c,
Assert::allContains($d, 'bar');
}

public function implementsInterface($a, string $b, $c): void
{
Assert::implementsInterface($a, Bar::class);
Assert::implementsInterface($a, Bar::class);

Assert::implementsInterface($b, Bar::class);
Assert::implementsInterface($b, Bar::class);

Assert::implementsInterface($c, Unknown::class);
Assert::implementsInterface($c, self::class);
}

}

interface Bar {};
Expand Down
18 changes: 15 additions & 3 deletions tests/Type/WebMozartAssert/data/object.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,25 @@ public function interfaceExists($a, $b): void
assertType('class-string|null', $b);
}

public function implementsInterface($a, $b): void
public function implementsInterface($a, $b, string $c, object $d, $e, $f): void
{
Assert::implementsInterface($a, ObjectFoo::class);
assertType('PHPStan\Type\WebMozartAssert\ObjectFoo', $a);
assertType('class-string<PHPStan\Type\WebMozartAssert\ObjectFoo>|PHPStan\Type\WebMozartAssert\ObjectFoo', $a);

Assert::nullOrImplementsInterface($b, ObjectFoo::class);
assertType('PHPStan\Type\WebMozartAssert\ObjectFoo|null', $b);
assertType('class-string<PHPStan\Type\WebMozartAssert\ObjectFoo>|PHPStan\Type\WebMozartAssert\ObjectFoo|null', $b);

Assert::implementsInterface($c, ObjectFoo::class);
assertType('class-string<PHPStan\Type\WebMozartAssert\ObjectFoo>', $c);

Assert::implementsInterface($d, ObjectFoo::class);
assertType('PHPStan\Type\WebMozartAssert\ObjectFoo', $d);

Assert::implementsInterface($e, self::class);
assertType('mixed', $e);

Assert::implementsInterface($f, Unknown::class);
assertType('mixed', $f);
}

public function propertyExists(object $a): void
Expand Down