2022-01-16 21:45:58 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace Psalm\Tests;
|
|
|
|
|
|
|
|
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
|
|
|
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
|
|
|
|
|
|
|
class KeyOfArrayTest extends TestCase
|
|
|
|
{
|
|
|
|
use InvalidCodeAnalysisTestTrait;
|
|
|
|
use ValidCodeAnalysisTestTrait;
|
|
|
|
|
|
|
|
public function providerValidCodeParse(): iterable
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
'keyOfListClassConstant' => [
|
|
|
|
'code' => '<?php
|
|
|
|
class A {
|
|
|
|
const FOO = [
|
2022-01-20 22:41:33 +01:00
|
|
|
"bar"
|
2022-01-16 21:45:58 +01:00
|
|
|
];
|
|
|
|
/** @return key-of<A::FOO> */
|
|
|
|
public function getKey() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'
|
|
|
|
],
|
|
|
|
'keyOfAssociativeArrayClassConstant' => [
|
|
|
|
'code' => '<?php
|
|
|
|
class A {
|
|
|
|
const FOO = [
|
2022-01-20 22:41:33 +01:00
|
|
|
"bar" => 42
|
2022-01-16 21:45:58 +01:00
|
|
|
];
|
|
|
|
/** @return key-of<A::FOO> */
|
|
|
|
public function getKey() {
|
2022-01-20 22:41:33 +01:00
|
|
|
return "bar";
|
2022-01-16 21:45:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
'
|
|
|
|
],
|
|
|
|
'allKeysOfAssociativeArrayPossible' => [
|
|
|
|
'code' => '<?php
|
|
|
|
class A {
|
|
|
|
const FOO = [
|
2022-01-20 22:41:33 +01:00
|
|
|
"bar" => 42,
|
|
|
|
"adams" => 43,
|
2022-01-16 21:45:58 +01:00
|
|
|
];
|
|
|
|
/** @return key-of<A::FOO> */
|
|
|
|
public function getKey(bool $adams) {
|
|
|
|
if ($adams) {
|
2022-01-20 22:41:33 +01:00
|
|
|
return "adams";
|
2022-01-16 21:45:58 +01:00
|
|
|
}
|
2022-01-20 22:41:33 +01:00
|
|
|
return "bar";
|
2022-01-16 21:45:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
'
|
|
|
|
],
|
|
|
|
'keyOfAsArray' => [
|
|
|
|
'code' => '<?php
|
|
|
|
class A {
|
|
|
|
/** @var array */
|
|
|
|
const FOO = [
|
2022-01-20 22:41:33 +01:00
|
|
|
"bar" => 42,
|
|
|
|
"adams" => 43,
|
2022-01-16 21:45:58 +01:00
|
|
|
];
|
|
|
|
/** @return key-of<self::FOO>[] */
|
2022-01-20 22:41:33 +01:00
|
|
|
public function getKey() {
|
2022-01-16 21:45:58 +01:00
|
|
|
return array_keys(self::FOO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'
|
|
|
|
],
|
|
|
|
'keyOfArrayLiteral' => [
|
|
|
|
'code' => '<?php
|
|
|
|
/**
|
|
|
|
* @return key-of<array<int, string>>
|
|
|
|
*/
|
|
|
|
function getKey() {
|
|
|
|
return 32;
|
|
|
|
}
|
|
|
|
'
|
|
|
|
],
|
|
|
|
'keyOfUnionArrayLiteral' => [
|
|
|
|
'code' => '<?php
|
|
|
|
/**
|
|
|
|
* @return key-of<array<int, string>|array<float, string>>
|
|
|
|
*/
|
|
|
|
function getKey(bool $asFloat) {
|
|
|
|
if ($asFloat) {
|
|
|
|
return 42.0;
|
|
|
|
}
|
|
|
|
return 42;
|
|
|
|
}
|
|
|
|
'
|
|
|
|
],
|
2022-01-20 22:41:33 +01:00
|
|
|
'keyOfUnionListAndKeyedArray' => [
|
|
|
|
'code' => '<?php
|
|
|
|
/**
|
2022-11-12 02:14:21 +01:00
|
|
|
* @return key-of<list<int>|array{a: int, b: int}>
|
2022-01-20 22:41:33 +01:00
|
|
|
*/
|
|
|
|
function getKey(bool $asInt) {
|
|
|
|
if ($asInt) {
|
|
|
|
return 42;
|
|
|
|
}
|
|
|
|
return "a";
|
|
|
|
}
|
|
|
|
',
|
|
|
|
],
|
2022-01-16 21:45:58 +01:00
|
|
|
'keyOfListArrayLiteral' => [
|
|
|
|
'code' => '<?php
|
|
|
|
/**
|
|
|
|
* @return key-of<list<string>>
|
|
|
|
*/
|
|
|
|
function getKey() {
|
|
|
|
return 42;
|
|
|
|
}
|
|
|
|
'
|
|
|
|
],
|
|
|
|
'keyOfStringArrayConformsToString' => [
|
|
|
|
'code' => '<?php
|
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
function getKey2() {
|
|
|
|
/** @var key-of<array<string, string>>[] */
|
2022-01-20 22:41:33 +01:00
|
|
|
$keys2 = ["foo"];
|
2022-01-16 21:45:58 +01:00
|
|
|
return $keys2[0];
|
|
|
|
}
|
|
|
|
'
|
|
|
|
],
|
2022-02-21 23:02:27 +01:00
|
|
|
'keyOfExpandsPropertiesOf' => [
|
|
|
|
'code' => '<?php
|
|
|
|
class A {
|
|
|
|
/** @var bool */
|
|
|
|
public $foo = false;
|
|
|
|
/** @var string */
|
|
|
|
private $bar = "";
|
|
|
|
/** @var int */
|
|
|
|
protected $adams = 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return list<key-of<properties-of<A>>> */
|
|
|
|
function returnPropertyOfA() {
|
|
|
|
return ["foo", "bar", "adams"];
|
|
|
|
}
|
|
|
|
',
|
|
|
|
],
|
2022-01-16 21:45:58 +01:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
public function providerInvalidCodeParse(): iterable
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
'onlyDefinedKeysOfAssociativeArray' => [
|
|
|
|
'code' => '<?php
|
|
|
|
class A {
|
|
|
|
const FOO = [
|
2022-01-20 22:41:33 +01:00
|
|
|
"bar" => 42
|
2022-01-16 21:45:58 +01:00
|
|
|
];
|
|
|
|
/** @return key-of<A::FOO> */
|
2022-01-20 22:41:33 +01:00
|
|
|
public function getKey() {
|
|
|
|
return "adams";
|
2022-01-16 21:45:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
',
|
|
|
|
'error_message' => 'InvalidReturnStatement'
|
|
|
|
],
|
|
|
|
'keyOfArrayLiteral' => [
|
|
|
|
'code' => '<?php
|
|
|
|
class A {
|
|
|
|
/**
|
|
|
|
* @return key-of<array<int, string>>
|
|
|
|
*/
|
|
|
|
public function getKey() {
|
2022-01-20 22:41:33 +01:00
|
|
|
return "foo";
|
2022-01-16 21:45:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
',
|
|
|
|
'error_message' => 'InvalidReturnStatement'
|
|
|
|
],
|
|
|
|
'onlyIntAllowedForKeyOfList' => [
|
|
|
|
'code' => '<?php
|
|
|
|
class A {
|
|
|
|
/**
|
|
|
|
* @return key-of<list<string>>
|
|
|
|
*/
|
|
|
|
public function getKey() {
|
2022-01-20 22:41:33 +01:00
|
|
|
return "42";
|
2022-01-16 21:45:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
',
|
|
|
|
'error_message' => 'InvalidReturnStatement'
|
|
|
|
],
|
|
|
|
'noStringAllowedInKeyOfIntFloatArray' => [
|
|
|
|
'code' => '<?php
|
|
|
|
/**
|
|
|
|
* @return key-of<array<int, string>|array<float, string>>
|
|
|
|
*/
|
|
|
|
function getKey(bool $asFloat) {
|
|
|
|
if ($asFloat) {
|
|
|
|
return 42.0;
|
|
|
|
}
|
2022-01-20 22:41:33 +01:00
|
|
|
return "42";
|
|
|
|
}
|
|
|
|
',
|
|
|
|
'error_message' => 'InvalidReturnStatement'
|
|
|
|
],
|
|
|
|
'noLiteralCAllowedInKeyOfUnionListAndKeyedArray' => [
|
|
|
|
'code' => '<?php
|
|
|
|
/**
|
2022-11-12 02:14:21 +01:00
|
|
|
* @return key-of<list<int>|array{a: int, b: int}>
|
2022-01-20 22:41:33 +01:00
|
|
|
*/
|
|
|
|
function getKey() {
|
|
|
|
return "c";
|
2022-01-16 21:45:58 +01:00
|
|
|
}
|
|
|
|
',
|
|
|
|
'error_message' => 'InvalidReturnStatement'
|
|
|
|
],
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|