mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Normalize stringified type names (#2239)
This change makes stringified types more normalized. Concretely it sorts all union types, reconciled types, and sorts the keys within object-like types.
This commit is contained in:
parent
e8618371fb
commit
216f991b0c
@ -7,6 +7,7 @@ use function count;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function is_int;
|
||||
use function sort;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\StatementsSource;
|
||||
@ -70,47 +71,43 @@ class ObjectLike extends \Psalm\Type\Atomic
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$union_type_parts = array_map(
|
||||
/**
|
||||
* @param string|int $name
|
||||
* @param Union $type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function ($name, Union $type) {
|
||||
return $name . ($type->possibly_undefined ? '?' : '') . ': ' . $type;
|
||||
},
|
||||
array_keys($this->properties),
|
||||
$this->properties
|
||||
);
|
||||
sort($union_type_parts);
|
||||
/** @psalm-suppress MixedOperand */
|
||||
return static::KEY . '{' .
|
||||
implode(
|
||||
', ',
|
||||
array_map(
|
||||
/**
|
||||
* @param string|int $name
|
||||
* @param Union $type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function ($name, Union $type) {
|
||||
return $name . ($type->possibly_undefined ? '?' : '') . ': ' . $type;
|
||||
},
|
||||
array_keys($this->properties),
|
||||
$this->properties
|
||||
)
|
||||
) .
|
||||
'}';
|
||||
return static::KEY . '{' . implode(', ', $union_type_parts) . '}';
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
$union_type_parts = array_map(
|
||||
/**
|
||||
* @param string|int $name
|
||||
* @param Union $type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function ($name, Union $type) {
|
||||
return $name . ($type->possibly_undefined ? '?' : '') . ': ' . $type->getId();
|
||||
},
|
||||
array_keys($this->properties),
|
||||
$this->properties
|
||||
);
|
||||
sort($union_type_parts);
|
||||
/** @psalm-suppress MixedOperand */
|
||||
return static::KEY . '{' .
|
||||
implode(
|
||||
', ',
|
||||
array_map(
|
||||
/**
|
||||
* @param string|int $name
|
||||
* @param Union $type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function ($name, Union $type) {
|
||||
return $name . ($type->possibly_undefined ? '?' : '') . ': ' . $type->getId();
|
||||
},
|
||||
array_keys($this->properties),
|
||||
$this->properties
|
||||
)
|
||||
) .
|
||||
implode(', ', $union_type_parts) .
|
||||
'}'
|
||||
. ($this->previous_value_type
|
||||
? '<' . ($this->previous_key_type ? $this->previous_key_type->getId() . ', ' : '')
|
||||
|
@ -42,6 +42,7 @@ use Psalm\Type\Atomic\TScalar;
|
||||
use Psalm\Type\Atomic\TString;
|
||||
use Psalm\Type\Atomic\TTemplateParam;
|
||||
use Psalm\Type\Atomic\TTrue;
|
||||
use function sort;
|
||||
use function str_replace;
|
||||
use function str_split;
|
||||
use function strpos;
|
||||
@ -279,18 +280,15 @@ class Reconciler
|
||||
&& (!$has_isset || substr($key, -1, 1) !== ']')
|
||||
&& !($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer)
|
||||
) {
|
||||
$reconcile_key = implode(
|
||||
'&',
|
||||
array_map(
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function (array $new_type_part_parts) {
|
||||
return implode('|', $new_type_part_parts);
|
||||
},
|
||||
$new_type_parts
|
||||
)
|
||||
$reconciled_parts = array_map(
|
||||
function (array $new_type_part_parts): string {
|
||||
sort($new_type_part_parts);
|
||||
return implode('|', $new_type_part_parts);
|
||||
},
|
||||
$new_type_parts
|
||||
);
|
||||
sort($reconciled_parts);
|
||||
$reconcile_key = implode('&', $reconciled_parts);
|
||||
|
||||
self::triggerIssueForImpossible(
|
||||
$result_type,
|
||||
|
@ -7,6 +7,7 @@ use function array_shift;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function is_string;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
@ -26,7 +27,9 @@ use Psalm\Type\Atomic\TNamedObject;
|
||||
use Psalm\Type\Atomic\TString;
|
||||
use Psalm\Type\Atomic\TTemplateParam;
|
||||
use function reset;
|
||||
use function sort;
|
||||
use function strpos;
|
||||
use function strval;
|
||||
use function substr;
|
||||
|
||||
class Union
|
||||
@ -284,10 +287,7 @@ class Union
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
if (empty($this->types)) {
|
||||
return '';
|
||||
}
|
||||
$s = '';
|
||||
$types = [];
|
||||
|
||||
$printed_int = false;
|
||||
$printed_float = false;
|
||||
@ -314,18 +314,16 @@ class Union
|
||||
$printed_int = true;
|
||||
}
|
||||
|
||||
$s .= $type . '|';
|
||||
$types[] = strval($type);
|
||||
}
|
||||
|
||||
return substr($s, 0, -1) ?: '';
|
||||
sort($types);
|
||||
return implode('|', $types);
|
||||
}
|
||||
|
||||
public function getKey() : string
|
||||
{
|
||||
if (empty($this->types)) {
|
||||
return '';
|
||||
}
|
||||
$s = '';
|
||||
$types = [];
|
||||
|
||||
$printed_int = false;
|
||||
$printed_float = false;
|
||||
@ -337,28 +335,29 @@ class Union
|
||||
continue;
|
||||
}
|
||||
|
||||
$s .= 'float|';
|
||||
$types[] = 'float';
|
||||
$printed_float = true;
|
||||
} elseif ($type instanceof TLiteralString) {
|
||||
if ($printed_string) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$s .= 'string|';
|
||||
$types[] = 'string';
|
||||
$printed_string = true;
|
||||
} elseif ($type instanceof TLiteralInt) {
|
||||
if ($printed_int) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$s .= 'int|';
|
||||
$types[] = 'int';
|
||||
$printed_int = true;
|
||||
} else {
|
||||
$s .= $type->getKey() . '|';
|
||||
$types[] = strval($type->getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return substr($s, 0, -1) ?: '';
|
||||
sort($types);
|
||||
return implode('|', $types);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -370,12 +369,12 @@ class Union
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
$s = '';
|
||||
$types = [];
|
||||
foreach ($this->types as $type) {
|
||||
$s .= $type->getId() . '|';
|
||||
$types[] = strval($type->getId());
|
||||
}
|
||||
|
||||
$id = substr($s, 0, -1);
|
||||
sort($types);
|
||||
$id = implode('|', $types);
|
||||
|
||||
$this->id = $id;
|
||||
|
||||
@ -409,7 +408,7 @@ class Union
|
||||
$printed_float = false;
|
||||
$printed_string = false;
|
||||
|
||||
$s = '';
|
||||
$types = [];
|
||||
|
||||
foreach ($this->types as $type) {
|
||||
$type_string = $type->toNamespacedString($namespace, $aliased_classes, $this_class, $use_phpdoc_format);
|
||||
@ -434,10 +433,11 @@ class Union
|
||||
$printed_int = true;
|
||||
}
|
||||
|
||||
$s .= $type_string . '|';
|
||||
$types[] = $type_string;
|
||||
}
|
||||
|
||||
return substr($s, 0, -1) ?: '';
|
||||
sort($types);
|
||||
return implode('|', $types);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ class ArgTest extends TestCase
|
||||
sort($b);
|
||||
',
|
||||
'assertions' => [
|
||||
'$a' => 'array{b: int, a: int}',
|
||||
'$a' => 'array{a: int, b: int}',
|
||||
'$b' => 'array<int, int>',
|
||||
],
|
||||
],
|
||||
@ -37,8 +37,8 @@ class ArgTest extends TestCase
|
||||
array_push($b, (bool)rand(0, 1));
|
||||
',
|
||||
'assertions' => [
|
||||
'$a' => 'array<string|int, int|bool>',
|
||||
'$b' => 'array<string|int, int|bool>',
|
||||
'$a' => 'array<int|string, bool|int>',
|
||||
'$b' => 'array<int|string, bool|int>',
|
||||
],
|
||||
],
|
||||
'byRefArgAssignment' => [
|
||||
|
@ -373,7 +373,7 @@ class ArrayAccessTest extends TestCase
|
||||
$doc->loadXML("<node key=\"value\"/>");
|
||||
$e = $doc->getElementsByTagName("node")[0];',
|
||||
[
|
||||
'$e' => 'null|DOMElement',
|
||||
'$e' => 'DOMElement|null',
|
||||
],
|
||||
],
|
||||
'getOnArrayAcccess' => [
|
||||
|
@ -130,7 +130,7 @@ class ArrayAssignmentTest extends TestCase
|
||||
break;
|
||||
}',
|
||||
'assertions' => [
|
||||
'$out' => 'list<string|int>',
|
||||
'$out' => 'list<int|string>',
|
||||
],
|
||||
],
|
||||
'genericArrayCreationWithElementsAddedInSwitchWithNothing' => [
|
||||
@ -150,7 +150,7 @@ class ArrayAssignmentTest extends TestCase
|
||||
// do nothing
|
||||
}',
|
||||
'assertions' => [
|
||||
'$out' => 'list<string|int>',
|
||||
'$out' => 'list<int|string>',
|
||||
],
|
||||
],
|
||||
'implicit2dIntArrayCreation' => [
|
||||
@ -455,7 +455,7 @@ class ArrayAssignmentTest extends TestCase
|
||||
$a["a"] = 5;
|
||||
$a[0] = 3;',
|
||||
'assertions' => [
|
||||
'$a' => 'array{a: int, 0: int}',
|
||||
'$a' => 'array{0: int, a: int}',
|
||||
],
|
||||
],
|
||||
'updateStringIntKey2' => [
|
||||
@ -467,7 +467,7 @@ class ArrayAssignmentTest extends TestCase
|
||||
$b[$string] = 5;
|
||||
$b[0] = 3;',
|
||||
'assertions' => [
|
||||
'$b' => 'array{c: int, 0: int}',
|
||||
'$b' => 'array{0: int, c: int}',
|
||||
],
|
||||
],
|
||||
'updateStringIntKey3' => [
|
||||
@ -504,7 +504,7 @@ class ArrayAssignmentTest extends TestCase
|
||||
$e[$int] = 3;
|
||||
$e[$string] = 5;',
|
||||
'assertions' => [
|
||||
'$e' => 'non-empty-array<string|int, int>',
|
||||
'$e' => 'non-empty-array<int|string, int>',
|
||||
],
|
||||
],
|
||||
'updateStringIntKeyWithIntRootAndNumberOffset' => [
|
||||
@ -517,7 +517,7 @@ class ArrayAssignmentTest extends TestCase
|
||||
$a[0]["a"] = 5;
|
||||
$a[0][0] = 3;',
|
||||
'assertions' => [
|
||||
'$a' => 'array{0: array{a: int, 0: int}}',
|
||||
'$a' => 'array{0: array{0: int, a: int}}',
|
||||
],
|
||||
],
|
||||
'updateStringIntKeyWithIntRoot' => [
|
||||
@ -545,10 +545,10 @@ class ArrayAssignmentTest extends TestCase
|
||||
$e[0][$int] = 3;
|
||||
$e[0][$string] = 5;',
|
||||
'assertions' => [
|
||||
'$b' => 'array{0: non-empty-array<string|int, int>}',
|
||||
'$b' => 'array{0: non-empty-array<int|string, int>}',
|
||||
'$c' => 'array{0: non-empty-array<int|string, int>}',
|
||||
'$d' => 'array{0: non-empty-array<int|string, int>}',
|
||||
'$e' => 'array{0: non-empty-array<string|int, int>}',
|
||||
'$e' => 'array{0: non-empty-array<int|string, int>}',
|
||||
],
|
||||
],
|
||||
'updateStringIntKeyWithObjectLikeRootAndNumberOffset' => [
|
||||
@ -561,7 +561,7 @@ class ArrayAssignmentTest extends TestCase
|
||||
$a["root"]["a"] = 5;
|
||||
$a["root"][0] = 3;',
|
||||
'assertions' => [
|
||||
'$a' => 'array{root: array{a: int, 0: int}}',
|
||||
'$a' => 'array{root: array{0: int, a: int}}',
|
||||
],
|
||||
],
|
||||
'updateStringIntKeyWithObjectLikeRoot' => [
|
||||
@ -589,10 +589,10 @@ class ArrayAssignmentTest extends TestCase
|
||||
$e["root"][$int] = 3;
|
||||
$e["root"][$string] = 5;',
|
||||
'assertions' => [
|
||||
'$b' => 'array{root: non-empty-array<string|int, int>}',
|
||||
'$b' => 'array{root: non-empty-array<int|string, int>}',
|
||||
'$c' => 'array{root: non-empty-array<int|string, int>}',
|
||||
'$d' => 'array{root: non-empty-array<int|string, int>}',
|
||||
'$e' => 'array{root: non-empty-array<string|int, int>}',
|
||||
'$e' => 'array{root: non-empty-array<int|string, int>}',
|
||||
],
|
||||
],
|
||||
'mixedArrayAssignmentWithStringKeys' => [
|
||||
@ -812,7 +812,7 @@ class ArrayAssignmentTest extends TestCase
|
||||
$a_keys = array_keys($a);',
|
||||
'assertions' => [
|
||||
'$a' => 'array{0: string, 1: int}',
|
||||
'$a_values' => 'non-empty-list<string|int>',
|
||||
'$a_values' => 'non-empty-list<int|string>',
|
||||
'$a_keys' => 'list<int>',
|
||||
],
|
||||
],
|
||||
@ -1019,7 +1019,7 @@ class ArrayAssignmentTest extends TestCase
|
||||
$a = (array) (rand(0, 1) ? [1 => "one"] : 0);
|
||||
$b = (array) null;',
|
||||
'assertions' => [
|
||||
'$a' => 'array{1?: string, 0?: int}',
|
||||
'$a' => 'array{0?: int, 1?: string}',
|
||||
'$b' => 'array<empty, empty>',
|
||||
],
|
||||
],
|
||||
|
@ -150,7 +150,7 @@ class ClassTest extends TestCase
|
||||
$class = mt_rand(0, 1) === 1 ? Foo::class : Bar::class;
|
||||
$object = new $class();',
|
||||
'assertions' => [
|
||||
'$object' => 'Foo|Bar',
|
||||
'$object' => 'Bar|Foo',
|
||||
],
|
||||
],
|
||||
'instantiateClassAndIsA' => [
|
||||
|
@ -156,10 +156,10 @@ class ClassMoveTest extends \Psalm\Tests\TestCase
|
||||
class B {}
|
||||
|
||||
/**
|
||||
* @param null|B $a
|
||||
* @param B|null $a
|
||||
* @param string | null $b
|
||||
* @param callable():B $c
|
||||
* @return null|B
|
||||
* @return B|null
|
||||
*/
|
||||
function foo(?B $a, $b, $c) : ?B {
|
||||
return $a;
|
||||
@ -338,7 +338,7 @@ class ClassMoveTest extends \Psalm\Tests\TestCase
|
||||
use Exception;
|
||||
|
||||
class B {
|
||||
/** @var null|Exception */
|
||||
/** @var Exception|null */
|
||||
public $x;
|
||||
|
||||
/**
|
||||
@ -403,12 +403,12 @@ class ClassMoveTest extends \Psalm\Tests\TestCase
|
||||
|
||||
/**
|
||||
* @param Bahh $b
|
||||
* @param Bahh::FOO|Bahh::FAR $c
|
||||
* @param Bahh::FAR|Bahh::FOO $c
|
||||
*/
|
||||
function doSomething(Bahh $b, int $c) : void {}
|
||||
|
||||
class A {
|
||||
/** @var null|Bahh */
|
||||
/** @var Bahh|null */
|
||||
public $x = null;
|
||||
}
|
||||
}
|
||||
@ -455,29 +455,29 @@ class ClassMoveTest extends \Psalm\Tests\TestCase
|
||||
public $x = null;
|
||||
/** @var ?B */
|
||||
public $y = null;
|
||||
/** @var null|A|B */
|
||||
/** @var A|B|null */
|
||||
public $z = null;
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
namespace Bar\Baz {
|
||||
class A {
|
||||
/** @var null|B */
|
||||
/** @var B|null */
|
||||
public $x = null;
|
||||
/** @var null|self */
|
||||
public $y = null;
|
||||
/** @var null|self|B|\Foo\C */
|
||||
/** @var B|\Foo\C|null|self */
|
||||
public $z = null;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Bar\Baz {
|
||||
class B {
|
||||
/** @var null|A */
|
||||
/** @var A|null */
|
||||
public $x = null;
|
||||
/** @var null|self */
|
||||
public $y = null;
|
||||
/** @var null|A|self|\Foo\C */
|
||||
/** @var A|\Foo\C|null|self */
|
||||
public $z = null;
|
||||
}
|
||||
}
|
||||
@ -487,11 +487,11 @@ class ClassMoveTest extends \Psalm\Tests\TestCase
|
||||
use Bar\Baz\B;
|
||||
|
||||
class C {
|
||||
/** @var null|A */
|
||||
/** @var A|null */
|
||||
public $x = null;
|
||||
/** @var null|B */
|
||||
/** @var B|null */
|
||||
public $y = null;
|
||||
/** @var null|A|B */
|
||||
/** @var A|B|null */
|
||||
public $z = null;
|
||||
}
|
||||
}',
|
||||
@ -534,12 +534,12 @@ class ClassMoveTest extends \Psalm\Tests\TestCase
|
||||
|
||||
/**
|
||||
* @param Kappa $b
|
||||
* @param Kappa::FOO|Kappa::FAR $c
|
||||
* @param Kappa::FAR|Kappa::FOO $c
|
||||
*/
|
||||
function doSomething(Kappa $b, int $c) : void {}
|
||||
|
||||
class A {
|
||||
/** @var null|Kappa */
|
||||
/** @var Kappa|null */
|
||||
public $x = null;
|
||||
}
|
||||
}
|
||||
|
@ -346,7 +346,7 @@ class MethodMoveTest extends \Psalm\Tests\TestCase
|
||||
/**
|
||||
* @param A $a1
|
||||
* Some description
|
||||
* @param null|A
|
||||
* @param A|null
|
||||
* $a2
|
||||
* @param array<int, A> $a3
|
||||
* @return A
|
||||
@ -424,7 +424,7 @@ class MethodMoveTest extends \Psalm\Tests\TestCase
|
||||
/**
|
||||
* @param A $a1
|
||||
* Some description
|
||||
* @param null|A
|
||||
* @param A|null
|
||||
* $a2
|
||||
* @param array<int, A> $a3
|
||||
* @return A
|
||||
@ -505,7 +505,7 @@ class MethodMoveTest extends \Psalm\Tests\TestCase
|
||||
/**
|
||||
* @param A $a1
|
||||
* Some description
|
||||
* @param null|A
|
||||
* @param A|null
|
||||
* $a2
|
||||
* @param array<int, A> $a3
|
||||
* @return A
|
||||
|
@ -120,22 +120,22 @@ class NamespaceMoveTest extends \Psalm\Tests\TestCase
|
||||
'<?php
|
||||
namespace Bar\Baz {
|
||||
class A {
|
||||
/** @var null|B */
|
||||
/** @var B|null */
|
||||
public $x = null;
|
||||
/** @var null|self */
|
||||
public $y = null;
|
||||
/** @var null|self|B|\Foo\C */
|
||||
/** @var B|\Foo\C|null|self */
|
||||
public $z = null;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Bar\Baz {
|
||||
class B {
|
||||
/** @var null|A */
|
||||
/** @var A|null */
|
||||
public $x = null;
|
||||
/** @var null|self */
|
||||
public $y = null;
|
||||
/** @var null|A|self|\Foo\C */
|
||||
/** @var A|\Foo\C|null|self */
|
||||
public $z = null;
|
||||
}
|
||||
}
|
||||
@ -145,11 +145,11 @@ class NamespaceMoveTest extends \Psalm\Tests\TestCase
|
||||
use Bar\Baz\B;
|
||||
|
||||
class C {
|
||||
/** @var null|A */
|
||||
/** @var A|null */
|
||||
public $x = null;
|
||||
/** @var null|B */
|
||||
/** @var B|null */
|
||||
public $y = null;
|
||||
/** @var null|A|B */
|
||||
/** @var A|B|null */
|
||||
public $z = null;
|
||||
}
|
||||
}',
|
||||
|
@ -202,7 +202,7 @@ class PropertyMoveTest extends \Psalm\Tests\TestCase
|
||||
class B {
|
||||
|
||||
|
||||
/** @var null|int */
|
||||
/** @var int|null */
|
||||
public static $fooBar;
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ class ReturnTypeManipulationTest extends FileManipulationTest
|
||||
}',
|
||||
'<?php
|
||||
/**
|
||||
* @return string|null
|
||||
* @return null|string
|
||||
*/
|
||||
function foo() {
|
||||
return rand(0, 1) ? "hello" : null;
|
||||
@ -110,7 +110,7 @@ class ReturnTypeManipulationTest extends FileManipulationTest
|
||||
}',
|
||||
'<?php
|
||||
/**
|
||||
* @return string|null
|
||||
* @return null|string
|
||||
*/
|
||||
function foo() {
|
||||
return rand(0, 1) ? "hello" : null;
|
||||
@ -265,7 +265,7 @@ class ReturnTypeManipulationTest extends FileManipulationTest
|
||||
}',
|
||||
'<?php
|
||||
/**
|
||||
* @return ((int[]|int)[]|int)[]
|
||||
* @return ((int|int[])[]|int)[]
|
||||
*
|
||||
* @psalm-return array{a: int, b: int, c: array{a: int, b: int, c: array{a: int, b: int, c: int}}}
|
||||
*/
|
||||
@ -544,7 +544,7 @@ class ReturnTypeManipulationTest extends FileManipulationTest
|
||||
}',
|
||||
'<?php
|
||||
/**
|
||||
* @return string|null
|
||||
* @return null|string
|
||||
*/
|
||||
function foo() {
|
||||
if (rand(0, 1)) {
|
||||
@ -555,7 +555,7 @@ class ReturnTypeManipulationTest extends FileManipulationTest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
* @return null|string
|
||||
*/
|
||||
function bar() {
|
||||
if (rand(0, 1)) {
|
||||
@ -793,7 +793,7 @@ class ReturnTypeManipulationTest extends FileManipulationTest
|
||||
}',
|
||||
'<?php
|
||||
/**
|
||||
* @return string|false
|
||||
* @return false|string
|
||||
*/
|
||||
function foo() {
|
||||
return rand(0, 1) ? "hello" : false;
|
||||
@ -809,7 +809,7 @@ class ReturnTypeManipulationTest extends FileManipulationTest
|
||||
}',
|
||||
'<?php
|
||||
/**
|
||||
* @return string|null
|
||||
* @return null|string
|
||||
*/
|
||||
function foo() {
|
||||
return rand(0, 1) ? "hello" : null;
|
||||
|
@ -125,7 +125,7 @@ class FunctionCallTest extends TestCase
|
||||
'assertions' => [
|
||||
'$a' => 'int',
|
||||
'$b' => 'float',
|
||||
'$c' => 'numeric|null',
|
||||
'$c' => 'null|numeric',
|
||||
],
|
||||
'error_levels' => ['MixedAssignment', 'MixedArgument'],
|
||||
],
|
||||
@ -244,21 +244,21 @@ class FunctionCallTest extends TestCase
|
||||
'<?php
|
||||
$d = array_reverse(["a", "b", 1, "d" => 4]);',
|
||||
'assertions' => [
|
||||
'$d' => 'non-empty-array<string|int, string|int>',
|
||||
'$d' => 'non-empty-array<int|string, int|string>',
|
||||
],
|
||||
],
|
||||
'arrayReverseDontPreserveKeyExplicitArg' => [
|
||||
'<?php
|
||||
$d = array_reverse(["a", "b", 1, "d" => 4], false);',
|
||||
'assertions' => [
|
||||
'$d' => 'non-empty-array<string|int, string|int>',
|
||||
'$d' => 'non-empty-array<int|string, int|string>',
|
||||
],
|
||||
],
|
||||
'arrayReversePreserveKey' => [
|
||||
'<?php
|
||||
$d = array_reverse(["a", "b", 1], true);',
|
||||
'assertions' => [
|
||||
'$d' => 'non-empty-array<int, string|int>',
|
||||
'$d' => 'non-empty-array<int, int|string>',
|
||||
],
|
||||
],
|
||||
'arrayDiff' => [
|
||||
@ -498,7 +498,7 @@ class FunctionCallTest extends TestCase
|
||||
$a[] = "hello";
|
||||
$b = array_pop($a);',
|
||||
'assertions' => [
|
||||
'$b' => 'string|mixed',
|
||||
'$b' => 'mixed|string',
|
||||
],
|
||||
'error_levels' => [
|
||||
'MixedAssignment',
|
||||
@ -603,7 +603,7 @@ class FunctionCallTest extends TestCase
|
||||
|
||||
foo($a3);',
|
||||
'assertions' => [
|
||||
'$a3' => 'array{hi: int, bye: int}',
|
||||
'$a3' => 'array{bye: int, hi: int}',
|
||||
],
|
||||
],
|
||||
'arrayRand' => [
|
||||
@ -688,7 +688,7 @@ class FunctionCallTest extends TestCase
|
||||
$b = getenv("some_key");',
|
||||
'assertions' => [
|
||||
'$a' => 'array<array-key, string>',
|
||||
'$b' => 'string|false',
|
||||
'$b' => 'false|string',
|
||||
],
|
||||
],
|
||||
'arrayPopNotNullable' => [
|
||||
@ -758,7 +758,7 @@ class FunctionCallTest extends TestCase
|
||||
}
|
||||
/**
|
||||
* @param string[] $arr
|
||||
* @return string|false
|
||||
* @return false|string
|
||||
*/
|
||||
function bat(array $arr) {
|
||||
return current($arr);
|
||||
@ -777,7 +777,7 @@ class FunctionCallTest extends TestCase
|
||||
return $a;
|
||||
}
|
||||
/**
|
||||
* @return string|false
|
||||
* @return false|string
|
||||
*/
|
||||
function bat(string $s) {
|
||||
return file_get_contents($s);
|
||||
@ -959,7 +959,7 @@ class FunctionCallTest extends TestCase
|
||||
$arr = ["one", "two", "three"];
|
||||
$n = next($arr);',
|
||||
'assertions' => [
|
||||
'$n' => 'string|false',
|
||||
'$n' => 'false|string',
|
||||
],
|
||||
],
|
||||
'iteratorToArray' => [
|
||||
@ -1038,7 +1038,7 @@ class FunctionCallTest extends TestCase
|
||||
'strtrWithPossiblyFalseFirstArg' => [
|
||||
'<?php
|
||||
/**
|
||||
* @param string|false $str
|
||||
* @param false|string $str
|
||||
* @param array<string, string> $replace_pairs
|
||||
* @return string
|
||||
*/
|
||||
@ -1270,7 +1270,7 @@ class FunctionCallTest extends TestCase
|
||||
$d = [1, 2, 3];
|
||||
array_splice($d, -1, 1);',
|
||||
'assertions' => [
|
||||
'$a' => 'non-empty-array<int, string|int>',
|
||||
'$a' => 'non-empty-array<int, int|string>',
|
||||
'$b' => 'array{0: string, 1: string, 2: string}',
|
||||
'$c' => 'array{0: int, 1: int, 2: int}',
|
||||
],
|
||||
@ -1298,7 +1298,7 @@ class FunctionCallTest extends TestCase
|
||||
'<?php
|
||||
$a = @file_get_contents("foo");',
|
||||
'assertions' => [
|
||||
'$a' => 'string|false',
|
||||
'$a' => 'false|string',
|
||||
],
|
||||
],
|
||||
'arraySlicePreserveKeys' => [
|
||||
@ -1512,7 +1512,7 @@ class FunctionCallTest extends TestCase
|
||||
'$b' => 'int',
|
||||
'$c' => 'float',
|
||||
'$d' => 'float',
|
||||
'$e' => 'int|float',
|
||||
'$e' => 'float|int',
|
||||
],
|
||||
],
|
||||
'hashInit70' => [
|
||||
@ -1630,8 +1630,8 @@ class FunctionCallTest extends TestCase
|
||||
$b = mktime($_GET["foo"]);
|
||||
$c = mktime(1, 2, 3);',
|
||||
'assertions' => [
|
||||
'$a' => 'int|false',
|
||||
'$b' => 'int|false',
|
||||
'$a' => 'false|int',
|
||||
'$b' => 'false|int',
|
||||
'$c' => 'int',
|
||||
],
|
||||
],
|
||||
@ -1684,9 +1684,9 @@ class FunctionCallTest extends TestCase
|
||||
'<?php
|
||||
sscanf("10:05:03", "%d:%d:%d", $hours, $minutes, $seconds);',
|
||||
'assertions' => [
|
||||
'$hours' => 'string|int|float',
|
||||
'$minutes' => 'string|int|float',
|
||||
'$seconds' => 'string|int|float',
|
||||
'$hours' => 'float|int|string',
|
||||
'$minutes' => 'float|int|string',
|
||||
'$seconds' => 'float|int|string',
|
||||
],
|
||||
],
|
||||
'inferArrayMapReturnType' => [
|
||||
|
@ -60,7 +60,7 @@ class IssetTest extends TestCase
|
||||
$b = rand(0, 10) > 5 ? "hello" : null;
|
||||
$a = $b ?? null;',
|
||||
'assertions' => [
|
||||
'$a' => 'string|null',
|
||||
'$a' => 'null|string',
|
||||
],
|
||||
],
|
||||
'issetKeyedOffset' => [
|
||||
@ -75,7 +75,7 @@ class IssetTest extends TestCase
|
||||
$foo["a"] = "hello";
|
||||
}',
|
||||
'assertions' => [
|
||||
'$foo[\'a\']' => 'string|mixed',
|
||||
'$foo[\'a\']' => 'mixed|string',
|
||||
],
|
||||
'error_levels' => [],
|
||||
],
|
||||
@ -102,7 +102,7 @@ class IssetTest extends TestCase
|
||||
|
||||
$foo["a"] = $foo["a"] ?? "hello";',
|
||||
'assertions' => [
|
||||
'$foo[\'a\']' => 'string|mixed',
|
||||
'$foo[\'a\']' => 'mixed|string',
|
||||
],
|
||||
'error_levels' => ['MixedAssignment'],
|
||||
],
|
||||
|
@ -136,7 +136,7 @@ echo $a;';
|
||||
'line_from' => 2,
|
||||
'line_to' => 2,
|
||||
'type' => 'MixedInferredReturnType',
|
||||
'message' => 'Could not verify return type \'string|null\' for psalmCanVerify',
|
||||
'message' => 'Could not verify return type \'null|string\' for psalmCanVerify',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => 'function psalmCanVerify(int $your_code): ?string {',
|
||||
|
@ -361,7 +361,7 @@ class ForeachTest extends \Psalm\Tests\TestCase
|
||||
}
|
||||
}',
|
||||
'assignments' => [
|
||||
'$tag' => 'string|null',
|
||||
'$tag' => 'null|string',
|
||||
],
|
||||
],
|
||||
'bleedVarIntoOuterContextWithBreakInIf' => [
|
||||
@ -375,7 +375,7 @@ class ForeachTest extends \Psalm\Tests\TestCase
|
||||
}
|
||||
}',
|
||||
'assignments' => [
|
||||
'$tag' => 'string|null',
|
||||
'$tag' => 'null|string',
|
||||
],
|
||||
],
|
||||
'bleedVarIntoOuterContextWithBreakInElseAndIntSet' => [
|
||||
@ -389,7 +389,7 @@ class ForeachTest extends \Psalm\Tests\TestCase
|
||||
}
|
||||
}',
|
||||
'assignments' => [
|
||||
'$tag' => 'string|int|null',
|
||||
'$tag' => 'int|null|string',
|
||||
],
|
||||
],
|
||||
'bleedVarIntoOuterContextWithRedefineAndBreak' => [
|
||||
|
@ -123,7 +123,7 @@ class WhileTest extends \Psalm\Tests\TestCase
|
||||
$a = $a->parent;
|
||||
}',
|
||||
'assertions' => [
|
||||
'$a' => 'null|A',
|
||||
'$a' => 'A|null',
|
||||
],
|
||||
],
|
||||
'loopWithNoParadox' => [
|
||||
|
@ -197,7 +197,7 @@ class MagicMethodAnnotationTest extends TestCase
|
||||
'$a' => 'string',
|
||||
'$b' => 'mixed',
|
||||
'$c' => 'bool',
|
||||
'$d' => 'array<array-key, string|int>',
|
||||
'$d' => 'array<array-key, int|string>',
|
||||
'$e' => 'callable():string',
|
||||
],
|
||||
],
|
||||
|
@ -101,7 +101,7 @@ class MethodCallTest extends TestCase
|
||||
|
||||
$b = (new DateTimeImmutable())->modify("+3 hours");',
|
||||
'assertions' => [
|
||||
'$yesterday' => 'false|MyDate',
|
||||
'$yesterday' => 'MyDate|false',
|
||||
'$b' => 'DateTimeImmutable',
|
||||
],
|
||||
],
|
||||
@ -182,7 +182,7 @@ class MethodCallTest extends TestCase
|
||||
$a = $xml->asXML();
|
||||
$b = $xml->asXML("foo.xml");',
|
||||
'assertions' => [
|
||||
'$a' => 'string|false',
|
||||
'$a' => 'false|string',
|
||||
'$b' => 'bool',
|
||||
],
|
||||
],
|
||||
|
@ -147,7 +147,7 @@ class MethodMutationTest extends TestCase
|
||||
'somefile.php'
|
||||
);
|
||||
|
||||
$this->assertSame('null|User', (string)$method_context->vars_in_scope['$this->user']);
|
||||
$this->assertSame('User|null', (string)$method_context->vars_in_scope['$this->user']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -641,7 +641,7 @@ class MethodSignatureTest extends TestCase
|
||||
return $s;
|
||||
}
|
||||
}',
|
||||
'error_message' => 'Argument 1 of B::foo has wrong type \'string\', expecting \'string|null\' as',
|
||||
'error_message' => 'Argument 1 of B::foo has wrong type \'string\', expecting \'null|string\' as',
|
||||
],
|
||||
'mismatchingCovariantReturn' => [
|
||||
'<?php
|
||||
|
@ -35,7 +35,7 @@ class Php55Test extends TestCase
|
||||
$a = $number;
|
||||
}',
|
||||
'assertions' => [
|
||||
'$a' => 'null|int',
|
||||
'$a' => 'int|null',
|
||||
],
|
||||
],
|
||||
'finally' => [
|
||||
|
@ -55,7 +55,7 @@ class Php70Test extends TestCase
|
||||
$arr = ["hello", "goodbye"];
|
||||
$a = $arr[rand(0, 10)] ?? null;',
|
||||
'assertions' => [
|
||||
'$a' => 'string|null',
|
||||
'$a' => 'null|string',
|
||||
],
|
||||
],
|
||||
'nullCoalesceWithNullableOnLeft' => [
|
||||
|
@ -21,7 +21,7 @@ class Php71Test extends TestCase
|
||||
|
||||
$a = a();',
|
||||
'assertions' => [
|
||||
'$a' => 'string|null',
|
||||
'$a' => 'null|string',
|
||||
],
|
||||
],
|
||||
'nullableReturnTypeInDocblock' => [
|
||||
|
@ -243,7 +243,7 @@ class PropertyTypeTest extends TestCase
|
||||
$b = $a->foo;
|
||||
}',
|
||||
'assertions' => [
|
||||
'$b' => 'null|int|string',
|
||||
'$b' => 'int|null|string',
|
||||
],
|
||||
],
|
||||
'sharedPropertyInElseIf' => [
|
||||
@ -270,7 +270,7 @@ class PropertyTypeTest extends TestCase
|
||||
$b = $a->foo;
|
||||
}',
|
||||
'assertions' => [
|
||||
'$b' => 'null|int|string',
|
||||
'$b' => 'int|null|string',
|
||||
],
|
||||
],
|
||||
'nullablePropertyCheck' => [
|
||||
@ -750,7 +750,7 @@ class PropertyTypeTest extends TestCase
|
||||
$node = new Node();
|
||||
$next = $node->next;',
|
||||
'assertions' => [
|
||||
'$next' => 'null|Node',
|
||||
'$next' => 'Node|null',
|
||||
],
|
||||
],
|
||||
'perPropertySuppress' => [
|
||||
|
@ -125,7 +125,7 @@ echo $a;';
|
||||
'line_from' => 2,
|
||||
'line_to' => 2,
|
||||
'type' => 'MixedInferredReturnType',
|
||||
'message' => 'Could not verify return type \'string|null\' for psalmCanVerify',
|
||||
'message' => 'Could not verify return type \'null|string\' for psalmCanVerify',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => 'function psalmCanVerify(int $your_code): ?string {',
|
||||
@ -210,7 +210,7 @@ echo $a;';
|
||||
'engineId' => 'Psalm',
|
||||
'ruleId' => 'MixedInferredReturnType',
|
||||
'primaryLocation' => [
|
||||
'message' => 'Could not verify return type \'string|null\' for psalmCanVerify',
|
||||
'message' => 'Could not verify return type \'null|string\' for psalmCanVerify',
|
||||
'filePath' => 'somefile.php',
|
||||
'textRange' => [
|
||||
'startLine' => 2,
|
||||
@ -277,7 +277,7 @@ echo $a;';
|
||||
|
||||
$this->assertSame(
|
||||
'somefile.php:3:10:error - Cannot find referenced variable $as_you
|
||||
somefile.php:2:42:error - Could not verify return type \'string|null\' for psalmCanVerify
|
||||
somefile.php:2:42:error - Could not verify return type \'null|string\' for psalmCanVerify
|
||||
somefile.php:7:6:error - Const CHANGE_ME is not defined
|
||||
somefile.php:15:6:warning - Possibly undefined global variable $a, first seen on line 10
|
||||
',
|
||||
@ -296,7 +296,7 @@ somefile.php:15:6:warning - Possibly undefined global variable $a, first seen on
|
||||
|
||||
$this->assertSame(
|
||||
'somefile.php:3: [E0001] UndefinedVariable: Cannot find referenced variable $as_you (column 10)
|
||||
somefile.php:2: [E0001] MixedInferredReturnType: Could not verify return type \'string|null\' for psalmCanVerify (column 42)
|
||||
somefile.php:2: [E0001] MixedInferredReturnType: Could not verify return type \'null|string\' for psalmCanVerify (column 42)
|
||||
somefile.php:7: [E0001] UndefinedConstant: Const CHANGE_ME is not defined (column 6)
|
||||
somefile.php:15: [W0001] PossiblyUndefinedGlobalVariable: Possibly undefined global variable $a, first seen on line 10 (column 6)
|
||||
',
|
||||
@ -318,7 +318,7 @@ somefile.php:15: [W0001] PossiblyUndefinedGlobalVariable: Possibly undefined glo
|
||||
'ERROR: UndefinedVariable - somefile.php:3:10 - Cannot find referenced variable $as_you
|
||||
return $as_you . "type";
|
||||
|
||||
ERROR: MixedInferredReturnType - somefile.php:2:42 - Could not verify return type \'string|null\' for psalmCanVerify
|
||||
ERROR: MixedInferredReturnType - somefile.php:2:42 - Could not verify return type \'null|string\' for psalmCanVerify
|
||||
function psalmCanVerify(int $your_code): ?string {
|
||||
|
||||
ERROR: UndefinedConstant - somefile.php:7:6 - Const CHANGE_ME is not defined
|
||||
@ -347,7 +347,7 @@ echo $a
|
||||
'ERROR: UndefinedVariable - somefile.php:3:10 - Cannot find referenced variable $as_you
|
||||
return $as_you . "type";
|
||||
|
||||
ERROR: MixedInferredReturnType - somefile.php:2:42 - Could not verify return type \'string|null\' for psalmCanVerify
|
||||
ERROR: MixedInferredReturnType - somefile.php:2:42 - Could not verify return type \'null|string\' for psalmCanVerify
|
||||
function psalmCanVerify(int $your_code): ?string {
|
||||
|
||||
ERROR: UndefinedConstant - somefile.php:7:6 - Const CHANGE_ME is not defined
|
||||
@ -373,7 +373,7 @@ echo CHANGE_ME;
|
||||
'ERROR: UndefinedVariable - somefile.php:3:10 - Cannot find referenced variable $as_you
|
||||
|
||||
|
||||
ERROR: MixedInferredReturnType - somefile.php:2:42 - Could not verify return type \'string|null\' for psalmCanVerify
|
||||
ERROR: MixedInferredReturnType - somefile.php:2:42 - Could not verify return type \'null|string\' for psalmCanVerify
|
||||
|
||||
|
||||
ERROR: UndefinedConstant - somefile.php:7:6 - Const CHANGE_ME is not defined
|
||||
@ -405,7 +405,7 @@ INFO: PossiblyUndefinedGlobalVariable - somefile.php:15:6 - Possibly undefined g
|
||||
'| SEVERITY | LINE | ISSUE | DESCRIPTION |' . "\n" .
|
||||
'+----------+------+---------------------------------+---------------------------------------------------------------+' . "\n" .
|
||||
'| ERROR | 3 | UndefinedVariable | Cannot find referenced variable $as_you |' . "\n" .
|
||||
'| ERROR | 2 | MixedInferredReturnType | Could not verify return type \'string|null\' for psalmCanVerify |' . "\n" .
|
||||
'| ERROR | 2 | MixedInferredReturnType | Could not verify return type \'null|string\' for psalmCanVerify |' . "\n" .
|
||||
'| ERROR | 7 | UndefinedConstant | Const CHANGE_ME is not defined |' . "\n" .
|
||||
'| INFO | 15 | PossiblyUndefinedGlobalVariable | Possibly undefined global variable $a, first seen on line 10 |' . "\n" .
|
||||
'+----------+------+---------------------------------+---------------------------------------------------------------+' . "\n",
|
||||
@ -428,7 +428,7 @@ INFO: PossiblyUndefinedGlobalVariable - somefile.php:15:6 - Possibly undefined g
|
||||
<error line="3" column="10" severity="error" message="UndefinedVariable: Cannot find referenced variable $as_you"/>
|
||||
</file>
|
||||
<file name="somefile.php">
|
||||
<error line="2" column="42" severity="error" message="MixedInferredReturnType: Could not verify return type \'string|null\' for psalmCanVerify"/>
|
||||
<error line="2" column="42" severity="error" message="MixedInferredReturnType: Could not verify return type \'null|string\' for psalmCanVerify"/>
|
||||
</file>
|
||||
<file name="somefile.php">
|
||||
<error line="7" column="6" severity="error" message="UndefinedConstant: Const CHANGE_ME is not defined"/>
|
||||
|
@ -291,7 +291,7 @@ class ReturnTypeTest extends TestCase
|
||||
|
||||
$blah = (new B())->blah();',
|
||||
'assertions' => [
|
||||
'$blah' => 'string|null',
|
||||
'$blah' => 'null|string',
|
||||
],
|
||||
],
|
||||
'overrideReturnTypeInGrandparent' => [
|
||||
@ -313,7 +313,7 @@ class ReturnTypeTest extends TestCase
|
||||
|
||||
$blah = (new C())->blah();',
|
||||
'assertions' => [
|
||||
'$blah' => 'string|null',
|
||||
'$blah' => 'null|string',
|
||||
],
|
||||
],
|
||||
'backwardsReturnType' => [
|
||||
|
@ -167,7 +167,7 @@ class ScopeTest extends TestCase
|
||||
throw new \Exception("bad");
|
||||
}',
|
||||
'assertions' => [
|
||||
'$a' => 'string|null',
|
||||
'$a' => 'null|string',
|
||||
],
|
||||
],
|
||||
'repeatAssertionWithOther' => [
|
||||
@ -180,7 +180,7 @@ class ScopeTest extends TestCase
|
||||
}
|
||||
}',
|
||||
'assertions' => [
|
||||
'$a' => 'string|null',
|
||||
'$a' => 'null|string',
|
||||
],
|
||||
'error_levels' => ['PossiblyFalseArgument'],
|
||||
],
|
||||
|
@ -536,7 +536,7 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
$b = $a->findOne();',
|
||||
[
|
||||
'$a' => 'AnotherRepo',
|
||||
'$b' => 'null|SpecificEntity',
|
||||
'$b' => 'SpecificEntity|null',
|
||||
],
|
||||
],
|
||||
'templateExtendsTwiceAndBound' => [
|
||||
@ -562,7 +562,7 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
$b = $a->findOne();',
|
||||
[
|
||||
'$a' => 'SpecificRepo',
|
||||
'$b' => 'null|SpecificEntity',
|
||||
'$b' => 'SpecificEntity|null',
|
||||
],
|
||||
],
|
||||
'multipleArgConstraints' => [
|
||||
@ -1071,7 +1071,7 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
|
||||
$a = (new Service)->first();',
|
||||
[
|
||||
'$a' => 'null|int',
|
||||
'$a' => 'int|null',
|
||||
],
|
||||
],
|
||||
'splObjectStorage' => [
|
||||
|
@ -1258,7 +1258,7 @@ class ClassTemplateTest extends TestCase
|
||||
$a_or_b = $random_collection->get();',
|
||||
[
|
||||
'$random_collection' => 'C<A>|C<B>',
|
||||
'$a_or_b' => 'B|A',
|
||||
'$a_or_b' => 'A|B',
|
||||
],
|
||||
],
|
||||
'inferClosureParamTypeFromContext' => [
|
||||
@ -2110,7 +2110,7 @@ class ClassTemplateTest extends TestCase
|
||||
$mario = new CharacterRow(["id" => 5, "name" => "Mario", "height" => 3.5]);
|
||||
|
||||
$mario->ame = "Luigi";',
|
||||
'error_message' => 'InvalidArgument - src' . DIRECTORY_SEPARATOR . 'somefile.php:47:29 - Argument 1 of CharacterRow::__set expects string(id)|string(name)|string(height), string(ame) provided',
|
||||
'error_message' => 'InvalidArgument - src' . DIRECTORY_SEPARATOR . 'somefile.php:47:29 - Argument 1 of CharacterRow::__set expects string(height)|string(id)|string(name), string(ame) provided',
|
||||
],
|
||||
'specialiseTypeBeforeReturning' => [
|
||||
'<?php
|
||||
|
@ -52,7 +52,7 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
|
||||
$a = (new I)->loader(FooChild::class);',
|
||||
'assertions' => [
|
||||
'$a' => 'null|FooChild',
|
||||
'$a' => 'FooChild|null',
|
||||
],
|
||||
],
|
||||
'upcastIterableToTraversable' => [
|
||||
|
@ -190,7 +190,7 @@ class FunctionTemplateTest extends TestCase
|
||||
$b = ["a" => 5, "c" => 6];
|
||||
$a = my_array_pop($b);',
|
||||
'assertions' => [
|
||||
'$a' => 'null|int',
|
||||
'$a' => 'int|null',
|
||||
'$b' => 'array<string, int>',
|
||||
],
|
||||
],
|
||||
|
@ -129,7 +129,7 @@ class TypeCombinationTest extends TestCase
|
||||
],
|
||||
],
|
||||
'intOrTrueOrFalseToBool' => [
|
||||
'int|bool',
|
||||
'bool|int',
|
||||
[
|
||||
'int',
|
||||
'false',
|
||||
@ -137,7 +137,7 @@ class TypeCombinationTest extends TestCase
|
||||
],
|
||||
],
|
||||
'intOrBoolOrTrueToBool' => [
|
||||
'int|bool',
|
||||
'bool|int',
|
||||
[
|
||||
'int',
|
||||
'bool',
|
||||
@ -145,7 +145,7 @@ class TypeCombinationTest extends TestCase
|
||||
],
|
||||
],
|
||||
'intOrTrueOrBoolToBool' => [
|
||||
'int|bool',
|
||||
'bool|int',
|
||||
[
|
||||
'int',
|
||||
'true',
|
||||
@ -188,7 +188,7 @@ class TypeCombinationTest extends TestCase
|
||||
],
|
||||
],
|
||||
'arrayMixedOrStringKeys' => [
|
||||
'array<int|string|mixed, string>',
|
||||
'array<int|mixed|string, string>',
|
||||
[
|
||||
'array<int|string,string>',
|
||||
'array<mixed,string>',
|
||||
@ -202,7 +202,7 @@ class TypeCombinationTest extends TestCase
|
||||
],
|
||||
],
|
||||
'arrayBigCombination' => [
|
||||
'array<array-key, int|float|string>',
|
||||
'array<array-key, float|int|string>',
|
||||
[
|
||||
'array<int|float>',
|
||||
'array<string>',
|
||||
@ -244,49 +244,49 @@ class TypeCombinationTest extends TestCase
|
||||
],
|
||||
],
|
||||
'arrayTraversableToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
'iterable<int, bool|string>',
|
||||
[
|
||||
'array<int, string>',
|
||||
'Traversable<int, bool>',
|
||||
],
|
||||
],
|
||||
'arrayIterableToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
'iterable<int, bool|string>',
|
||||
[
|
||||
'array<int, string>',
|
||||
'iterable<int, bool>',
|
||||
],
|
||||
],
|
||||
'iterableArrayToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
'iterable<int, bool|string>',
|
||||
[
|
||||
'iterable<int, string>',
|
||||
'array<int, bool>',
|
||||
],
|
||||
],
|
||||
'traversableIterableToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
'iterable<int, bool|string>',
|
||||
[
|
||||
'Traversable<int, string>',
|
||||
'iterable<int, bool>',
|
||||
],
|
||||
],
|
||||
'iterableTraversableToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
'iterable<int, bool|string>',
|
||||
[
|
||||
'iterable<int, string>',
|
||||
'Traversable<int, bool>',
|
||||
],
|
||||
],
|
||||
'arrayObjectAndParamsWithEmptyArray' => [
|
||||
'array<empty, empty>|ArrayObject<int, string>',
|
||||
'ArrayObject<int, string>|array<empty, empty>',
|
||||
[
|
||||
'ArrayObject<int, string>',
|
||||
'array<empty, empty>',
|
||||
],
|
||||
],
|
||||
'emptyArrayWithArrayObjectAndParams' => [
|
||||
'array<empty, empty>|ArrayObject<int, string>',
|
||||
'ArrayObject<int, string>|array<empty, empty>',
|
||||
[
|
||||
'array<empty, empty>',
|
||||
'ArrayObject<int, string>',
|
||||
@ -319,7 +319,7 @@ class TypeCombinationTest extends TestCase
|
||||
],
|
||||
],
|
||||
'aAndAOfB' => [
|
||||
'A<B>|A',
|
||||
'A|A<B>',
|
||||
[
|
||||
'A',
|
||||
'A<B>',
|
||||
@ -340,28 +340,28 @@ class TypeCombinationTest extends TestCase
|
||||
],
|
||||
],
|
||||
'combineObjectTypeWithIntKeyedArray' => [
|
||||
'array<int|string, string|int>',
|
||||
'array<int|string, int|string>',
|
||||
[
|
||||
'array{a: int}',
|
||||
'array<int, string>',
|
||||
],
|
||||
],
|
||||
'combineNestedObjectTypeWithObjectLikeIntKeyedArray' => [
|
||||
'array{a: array<int|string, string|int>}',
|
||||
'array{a: array<int|string, int|string>}',
|
||||
[
|
||||
'array{a: array{a: int}}',
|
||||
'array{a: array<int, string>}',
|
||||
],
|
||||
],
|
||||
'combineIntKeyedObjectTypeWithNestedIntKeyedArray' => [
|
||||
'array<int, array<int|string, string|int>>',
|
||||
'array<int, array<int|string, int|string>>',
|
||||
[
|
||||
'array<int, array{a:int}>',
|
||||
'array<int, array<int, string>>',
|
||||
],
|
||||
],
|
||||
'combineNestedObjectTypeWithNestedIntKeyedArray' => [
|
||||
'array<int|string, array<int|string, string|int>>',
|
||||
'array<int|string, array<int|string, int|string>>',
|
||||
[
|
||||
'array{a: array{a: int}}',
|
||||
'array<int, array<int, string>>',
|
||||
|
@ -41,7 +41,7 @@ class TypeParseTest extends TestCase
|
||||
*/
|
||||
public function testThisToStaticUnion()
|
||||
{
|
||||
$this->assertSame('static|A', (string) Type::parseString('$this|A'));
|
||||
$this->assertSame('A|static', (string) Type::parseString('$this|A'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,7 +81,7 @@ class TypeParseTest extends TestCase
|
||||
*/
|
||||
public function testNullableUnion()
|
||||
{
|
||||
$this->assertSame('string|int|null', (string) Type::parseString('?(string|int)'));
|
||||
$this->assertSame('int|null|string', (string) Type::parseString('?(string|int)'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,7 +97,7 @@ class TypeParseTest extends TestCase
|
||||
*/
|
||||
public function testNullableOrNullable()
|
||||
{
|
||||
$this->assertSame('string|int|null', (string) Type::parseString('?string|?int'));
|
||||
$this->assertSame('int|null|string', (string) Type::parseString('?string|?int'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,7 +170,7 @@ class TypeParseTest extends TestCase
|
||||
*/
|
||||
public function testIntersectionOrNull()
|
||||
{
|
||||
$this->assertSame('null|I1&I2', (string) Type::parseString('I1&I2|null'));
|
||||
$this->assertSame('I1&I2|null', (string) Type::parseString('I1&I2|null'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,7 +178,7 @@ class TypeParseTest extends TestCase
|
||||
*/
|
||||
public function testNullOrIntersection()
|
||||
{
|
||||
$this->assertSame('null|I1&I2', (string) Type::parseString('null|I1&I2'));
|
||||
$this->assertSame('I1&I2|null', (string) Type::parseString('null|I1&I2'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,7 +195,7 @@ class TypeParseTest extends TestCase
|
||||
public function testTraversableAndIteratorOrNull()
|
||||
{
|
||||
$this->assertSame(
|
||||
'null|Traversable&Iterator<int>',
|
||||
'Traversable&Iterator<int>|null',
|
||||
(string) Type::parseString('Traversable&Iterator<int>|null')
|
||||
);
|
||||
}
|
||||
@ -277,7 +277,7 @@ class TypeParseTest extends TestCase
|
||||
*/
|
||||
public function testPhpDocUnionOfArraysOrObject()
|
||||
{
|
||||
$this->assertSame('array<array-key, A|B>|C', (string) Type::parseString('A[]|B[]|C'));
|
||||
$this->assertSame('C|array<array-key, A|B>', (string) Type::parseString('A[]|B[]|C'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -380,7 +380,7 @@ class TypeParseTest extends TestCase
|
||||
public function testObjectLikeWithGenericArgs()
|
||||
{
|
||||
$this->assertSame(
|
||||
'array{a: array<int, string|int>, b: string}',
|
||||
'array{a: array<int, int|string>, b: string}',
|
||||
(string) Type::parseString('array{a: array<int, string|int>, b: string}')
|
||||
);
|
||||
}
|
||||
@ -804,7 +804,7 @@ class TypeParseTest extends TestCase
|
||||
*/
|
||||
public function testVeryLargeType()
|
||||
{
|
||||
$very_large_type = 'array{a: Closure():(array<mixed, mixed>|null), b?: Closure():array<mixed, mixed>, c?: Closure():array<mixed, mixed>, d?: Closure():array<mixed, mixed>, e?: Closure():(array{f: null|string, g: null|string, h: null|string, i: string, j: mixed, k: mixed, l: mixed, m: mixed, n: bool, o?: array{0: string}}|null), p?: Closure():(array{f: null|string, g: null|string, h: null|string, q: string, i: string, j: mixed, k: mixed, l: mixed, m: mixed, n: bool, o?: array{0: string}}|null), r?: Closure():(array<mixed, mixed>|null), s: array<mixed, mixed>}|null';
|
||||
$very_large_type = 'array{a: Closure():(array<mixed, mixed>|null), b?: Closure():array<mixed, mixed>, c?: Closure():array<mixed, mixed>, d?: Closure():array<mixed, mixed>, e?: Closure():(array{f: null|string, g: null|string, h: null|string, i: string, j: mixed, k: mixed, l: mixed, m: mixed, n: bool, o?: array{0: string}}|null), p?: Closure():(array{f: null|string, g: null|string, h: null|string, i: string, j: mixed, k: mixed, l: mixed, m: mixed, n: bool, o?: array{0: string}}|null), q: string, r?: Closure():(array<mixed, mixed>|null), s: array<mixed, mixed>}|null';
|
||||
|
||||
$this->assertSame(
|
||||
$very_large_type,
|
||||
|
@ -91,7 +91,7 @@ class TypeReconciliationTest extends TestCase
|
||||
return [
|
||||
'notNullWithObject' => ['MyObject', '!null', 'MyObject'],
|
||||
'notNullWithObjectPipeNull' => ['MyObject', '!null', 'MyObject|null'],
|
||||
'notNullWithMyObjectPipeFalse' => ['false|MyObject', '!null', 'MyObject|false'],
|
||||
'notNullWithMyObjectPipeFalse' => ['MyObject|false', '!null', 'MyObject|false'],
|
||||
'notNullWithMixed' => ['mixed', '!null', 'mixed'],
|
||||
|
||||
'notEmptyWithMyObject' => ['MyObject', '!falsy', 'MyObject'],
|
||||
@ -127,14 +127,14 @@ class TypeReconciliationTest extends TestCase
|
||||
|
||||
'nullableClassString' => ['null', 'falsy', '?class-string'],
|
||||
'mixedOrNullNotFalsy' => ['non-empty-mixed', '!falsy', 'mixed|null'],
|
||||
'mixedOrNullFalsy' => ['null|empty-mixed', 'falsy', 'mixed|null'],
|
||||
'mixedOrNullFalsy' => ['empty-mixed|null', 'falsy', 'mixed|null'],
|
||||
'nullableClassStringFalsy' => ['null', 'falsy', 'class-string<A>|null'],
|
||||
'nullableClassStringEqualsNull' => ['null', '=null', 'class-string<A>|null'],
|
||||
'nullableClassStringTruthy' => ['class-string<A>', '!falsy', 'class-string<A>|null'],
|
||||
'iterableToArray' => ['array<int, int>', 'array', 'iterable<int, int>'],
|
||||
'iterableToTraversable' => ['Traversable<int, int>', 'Traversable', 'iterable<int, int>'],
|
||||
'callableToCallableArray' => ['callable-array{0: string|object, 1: string}', 'array', 'callable'],
|
||||
'callableOrArrayToCallableArray' => ['array<array-key, mixed>|callable-array{0: string|object, 1: string}', 'array', 'callable|array'],
|
||||
'callableToCallableArray' => ['callable-array{0: object|string, 1: string}', 'array', 'callable'],
|
||||
'callableOrArrayToCallableArray' => ['array<array-key, mixed>|callable-array{0: object|string, 1: string}', 'array', 'callable|array'],
|
||||
'traversableToIntersection' => ['Countable&Traversable', 'Traversable', 'Countable'],
|
||||
'iterableWithoutParamsToTraversableWithoutParams' => ['Traversable', '!array', 'iterable'],
|
||||
'iterableWithParamsToTraversableWithParams' => ['Traversable<int, string>', '!array', 'iterable<int, string>'],
|
||||
@ -239,7 +239,7 @@ class TypeReconciliationTest extends TestCase
|
||||
$out = $a;
|
||||
}',
|
||||
'assertions' => [
|
||||
'$out' => 'null|A',
|
||||
'$out' => 'A|null',
|
||||
],
|
||||
],
|
||||
'notInstanceOfProperty' => [
|
||||
@ -268,7 +268,7 @@ class TypeReconciliationTest extends TestCase
|
||||
$out = $a->foo;
|
||||
}',
|
||||
'assertions' => [
|
||||
'$out' => 'null|B',
|
||||
'$out' => 'B|null',
|
||||
],
|
||||
'error_levels' => [],
|
||||
],
|
||||
@ -297,7 +297,7 @@ class TypeReconciliationTest extends TestCase
|
||||
$out = $a->foo;
|
||||
}',
|
||||
'assertions' => [
|
||||
'$out' => 'null|B',
|
||||
'$out' => 'B|null',
|
||||
],
|
||||
'error_levels' => [],
|
||||
],
|
||||
|
@ -731,7 +731,7 @@ class TypeTest extends TestCase
|
||||
|
||||
echo $var;',
|
||||
'assertions' => [
|
||||
'$var' => 'string|int',
|
||||
'$var' => 'int|string',
|
||||
],
|
||||
],
|
||||
'typeMixedAdjustment' => [
|
||||
@ -746,7 +746,7 @@ class TypeTest extends TestCase
|
||||
|
||||
echo $var;',
|
||||
'assertions' => [
|
||||
'$var' => 'string|int',
|
||||
'$var' => 'int|string',
|
||||
],
|
||||
],
|
||||
'typeAdjustmentIfNull' => [
|
||||
|
Loading…
Reference in New Issue
Block a user