2017-10-27 00:19:19 +02:00
|
|
|
<?php
|
|
|
|
namespace Psalm\Tests;
|
|
|
|
|
|
|
|
class AssertTest extends TestCase
|
|
|
|
{
|
|
|
|
use Traits\FileCheckerValidCodeParseTestTrait;
|
2018-02-25 17:30:45 +01:00
|
|
|
use Traits\FileCheckerInvalidCodeParseTestTrait;
|
2017-10-27 00:19:19 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function providerFileCheckerValidCodeParse()
|
|
|
|
{
|
|
|
|
return [
|
2018-02-23 21:39:33 +01:00
|
|
|
'assertInstanceOfB' => [
|
2017-10-27 00:19:19 +02:00
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
class B extends A {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function foo(): void {}
|
2017-10-27 00:19:19 +02:00
|
|
|
}
|
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
function assertInstanceOfB(A $var): void {
|
2017-10-27 00:19:19 +02:00
|
|
|
if (!$var instanceof B) {
|
|
|
|
throw new \Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-23 21:39:33 +01:00
|
|
|
function takesA(A $a): void {
|
|
|
|
assertInstanceOfB($a);
|
|
|
|
$a->foo();
|
|
|
|
}',
|
|
|
|
],
|
2018-02-25 17:30:45 +01:00
|
|
|
'assertInstanceOfInterface' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
|
|
|
public function bar() : void {}
|
|
|
|
}
|
|
|
|
interface I {
|
|
|
|
public function foo(): void;
|
|
|
|
}
|
|
|
|
class B extends A implements I {
|
|
|
|
public function foo(): void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
function assertInstanceOfI(A $var): void {
|
|
|
|
if (!$var instanceof I) {
|
|
|
|
throw new \Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function takesA(A $a): void {
|
|
|
|
assertInstanceOfI($a);
|
|
|
|
$a->bar();
|
|
|
|
$a->foo();
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'assertInstanceOfMultipleInterfaces' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
|
|
|
public function bar() : void {}
|
|
|
|
}
|
|
|
|
interface I1 {
|
|
|
|
public function foo1(): void;
|
|
|
|
}
|
|
|
|
interface I2 {
|
|
|
|
public function foo2(): void;
|
|
|
|
}
|
|
|
|
class B extends A implements I1, I2 {
|
|
|
|
public function foo1(): void {}
|
|
|
|
public function foo2(): void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
function assertInstanceOfInterfaces(A $var): void {
|
|
|
|
if (!$var instanceof I1 || !$var instanceof I2) {
|
|
|
|
throw new \Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function takesA(A $a): void {
|
|
|
|
assertInstanceOfInterfaces($a);
|
|
|
|
$a->bar();
|
|
|
|
$a->foo1();
|
|
|
|
}',
|
|
|
|
],
|
2018-02-23 21:39:33 +01:00
|
|
|
'assertInstanceOfBInClassMethod' => [
|
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
class B extends A {
|
|
|
|
public function foo(): void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
class C {
|
|
|
|
private function assertInstanceOfB(A $var): void {
|
|
|
|
if (!$var instanceof B) {
|
|
|
|
throw new \Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function takesA(A $a): void {
|
|
|
|
$this->assertInstanceOfB($a);
|
|
|
|
$a->foo();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'assertPropertyNotNull' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
|
|
|
public function foo(): void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
class B {
|
|
|
|
/** @var A|null */
|
|
|
|
public $a;
|
|
|
|
|
|
|
|
private function assertNotNullProperty(): void {
|
|
|
|
if (!$this->a) {
|
|
|
|
throw new \Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function takesA(A $a): void {
|
|
|
|
$this->assertNotNullProperty();
|
|
|
|
$a->foo();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'SKIPPED-assertInstanceOfClass' => [
|
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
class B extends A {
|
|
|
|
public function foo(): void {}
|
|
|
|
}
|
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
function assertInstanceOfClass(A $var, string $class): void {
|
2017-10-27 00:19:19 +02:00
|
|
|
if (!$var instanceof $class) {
|
|
|
|
throw new \Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
function takesA(A $a): void {
|
2018-02-23 21:39:33 +01:00
|
|
|
assertInstanceOfClass($a, B::class);
|
2017-10-27 00:19:19 +02:00
|
|
|
$a->foo();
|
|
|
|
}',
|
|
|
|
],
|
2018-05-28 21:07:42 +02:00
|
|
|
'assertInstanceOfBAnnotation' => [
|
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
class B extends A {
|
|
|
|
public function foo(): void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @psalm-assert B $var */
|
|
|
|
function myAssertInstanceOfB(A $var): void {
|
|
|
|
if (!$var instanceof B) {
|
|
|
|
throw new \Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function takesA(A $a): void {
|
|
|
|
myAssertInstanceOfB($a);
|
|
|
|
$a->foo();
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'assertIfTrueAnnotation' => [
|
|
|
|
'<?php
|
|
|
|
/** @psalm-assert-if-true string $myVar */
|
|
|
|
function isValidString(?string $myVar) : bool {
|
|
|
|
return $myVar !== null && $myVar[0] === "a";
|
|
|
|
}
|
|
|
|
|
|
|
|
$myString = rand(0, 1) ? "abacus" : null;
|
|
|
|
|
|
|
|
if (isValidString($myString)) {
|
|
|
|
echo "Ma chaine " . $myString;
|
|
|
|
}'
|
|
|
|
],
|
|
|
|
'assertIfFalseAnnotation' => [
|
|
|
|
'<?php
|
|
|
|
/** @psalm-assert-if-false string $myVar */
|
|
|
|
function isInvalidString(?string $myVar) : bool {
|
|
|
|
return $myVar === null || $myVar[0] !== "a";
|
|
|
|
}
|
|
|
|
|
|
|
|
$myString = rand(0, 1) ? "abacus" : null;
|
|
|
|
|
|
|
|
if (isInvalidString($myString)) {
|
|
|
|
// do something
|
|
|
|
} else {
|
|
|
|
echo "Ma chaine " . $myString;
|
|
|
|
}'
|
|
|
|
],
|
2017-10-27 00:19:19 +02:00
|
|
|
];
|
|
|
|
}
|
2018-02-25 17:30:45 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function providerFileCheckerInvalidCodeParse()
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
'assertInstanceOfMultipleInterfaces' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
|
|
|
public function bar() : void {}
|
|
|
|
}
|
|
|
|
interface I1 {
|
|
|
|
public function foo1(): void;
|
|
|
|
}
|
|
|
|
interface I2 {
|
|
|
|
public function foo2(): void;
|
|
|
|
}
|
|
|
|
class B extends A implements I1, I2 {
|
|
|
|
public function foo1(): void {}
|
|
|
|
public function foo2(): void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
function assertInstanceOfInterfaces(A $var): void {
|
|
|
|
if (!$var instanceof I1 && !$var instanceof I2) {
|
|
|
|
throw new \Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function takesA(A $a): void {
|
|
|
|
assertInstanceOfInterfaces($a);
|
|
|
|
$a->bar();
|
|
|
|
$a->foo1();
|
|
|
|
}',
|
|
|
|
'error_message' => 'UndefinedMethod',
|
|
|
|
],
|
2018-05-28 21:07:42 +02:00
|
|
|
'assertIfTrueNoAnnotation' => [
|
|
|
|
'<?php
|
|
|
|
function isValidString(?string $myVar) : bool {
|
|
|
|
return $myVar !== null && $myVar[0] === "a";
|
|
|
|
}
|
|
|
|
|
|
|
|
$myString = rand(0, 1) ? "abacus" : null;
|
|
|
|
|
|
|
|
if (isValidString($myString)) {
|
|
|
|
echo "Ma chaine " . $myString;
|
|
|
|
}',
|
|
|
|
'error_message' => 'PossiblyNullOperand',
|
|
|
|
],
|
|
|
|
'assertIfFalseNoAnnotation' => [
|
|
|
|
'<?php
|
|
|
|
function isInvalidString(?string $myVar) : bool {
|
|
|
|
return $myVar === null || $myVar[0] !== "a";
|
|
|
|
}
|
|
|
|
|
|
|
|
$myString = rand(0, 1) ? "abacus" : null;
|
|
|
|
|
|
|
|
if (isInvalidString($myString)) {
|
|
|
|
// do something
|
|
|
|
} else {
|
|
|
|
echo "Ma chaine " . $myString;
|
|
|
|
}',
|
|
|
|
'error_message' => 'PossiblyNullOperand',
|
|
|
|
],
|
2018-07-11 17:22:07 +02:00
|
|
|
'assertIfTrueMethodCall' => [
|
|
|
|
'<?php
|
|
|
|
class C {
|
|
|
|
/**
|
|
|
|
* @param mixed $p
|
|
|
|
* @psalm-assert-if-true int $p
|
|
|
|
*/
|
|
|
|
public function isInt($p): bool {
|
|
|
|
return is_int($p);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @param mixed $p
|
|
|
|
*/
|
|
|
|
public function doWork($p): void {
|
|
|
|
if ($this->isInt($p)) {
|
|
|
|
strlen($p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
'error_message' => 'InvalidScalarArgument',
|
|
|
|
],
|
2018-02-25 17:30:45 +01:00
|
|
|
];
|
|
|
|
}
|
2017-10-27 00:19:19 +02:00
|
|
|
}
|