Skip to content

Commit

Permalink
add error for invalid array key type in docblock
Browse files Browse the repository at this point in the history
  • Loading branch information
kkmuffme committed Dec 12, 2023
1 parent 1df5b35 commit 82ff582
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 14 deletions.
58 changes: 58 additions & 0 deletions src/Psalm/Internal/Type/TypeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNever;
use Psalm\Type\Atomic\TNonEmptyArray;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TObjectWithProperties;
use Psalm\Type\Atomic\TPropertiesOf;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateIndexedAccess;
use Psalm\Type\Atomic\TTemplateKeyOf;
use Psalm\Type\Atomic\TTemplateParam;
Expand Down Expand Up @@ -643,6 +645,34 @@ private static function getTypeFromGenericTree(
throw new TypeParseTreeException('Too many template parameters for array');
}

foreach ($generic_params[0]->getAtomicTypes() as $key => $atomic_type) {
if ($atomic_type instanceof TInt
|| $atomic_type instanceof TString
|| $atomic_type instanceof TArrayKey
|| $atomic_type instanceof TClassConstant // @todo resolve and check types
|| $atomic_type instanceof TMixed
|| $atomic_type instanceof TNever
|| $atomic_type instanceof TTemplateParam
|| $atomic_type instanceof TValueOf
) {
continue;
}

if ($codebase->register_stub_files || $codebase->register_autoload_files) {
$builder = $generic_params[0]->getBuilder();
$builder->removeType($key);

if (count($generic_params[0]->getAtomicTypes()) <= 1) {
$builder = $builder->addType(new TArrayKey($from_docblock));
}

$generic_params[0] = $builder->freeze();
continue;
}

throw new TypeParseTreeException('Invalid array key type ' . $atomic_type->getKey());
}

return new TArray($generic_params, $from_docblock);
}

Expand Down Expand Up @@ -671,6 +701,34 @@ private static function getTypeFromGenericTree(
throw new TypeParseTreeException('Too many template parameters for non-empty-array');
}

foreach ($generic_params[0]->getAtomicTypes() as $key => $atomic_type) {
if ($atomic_type instanceof TInt
|| $atomic_type instanceof TString
|| $atomic_type instanceof TArrayKey
|| $atomic_type instanceof TClassConstant // @todo resolve and check types
|| $atomic_type instanceof TMixed
|| $atomic_type instanceof TNever
|| $atomic_type instanceof TTemplateParam
|| $atomic_type instanceof TValueOf
) {
continue;
}

if ($codebase->register_stub_files || $codebase->register_autoload_files) {
$builder = $generic_params[0]->getBuilder();
$builder->removeType($key);

if (count($generic_params[0]->getAtomicTypes()) <= 1) {
$builder = $builder->addType(new TArrayKey($from_docblock));
}

$generic_params[0] = $builder->freeze();
continue;
}

throw new TypeParseTreeException('Invalid array key type ' . $atomic_type->getKey());
}

return new TNonEmptyArray($generic_params, null, null, 'non-empty-array', $from_docblock);
}

Expand Down
10 changes: 9 additions & 1 deletion tests/AnnotationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1366,7 +1366,15 @@ public function barBar() {
}',
'error_message' => 'MissingDocblockType',
],

'invalidArrayKeyType' => [
'code' => '<?php
/**
* @param array<float, string> $arg
* @return void
*/
function foo($arg) {}',
'error_message' => 'InvalidDocblock',
],
'invalidClassMethodReturnBrackets' => [
'code' => '<?php
class C {
Expand Down
9 changes: 5 additions & 4 deletions tests/ArrayAssignmentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ public function providerValidCodeParse(): iterable
'assignUnionOfLiterals' => [
'code' => '<?php
$result = [];
foreach (["a", "b"] as $k) {
$result[$k] = true;
}
$resultOpt = [];
foreach (["a", "b"] as $k) {
if (random_int(0, 1)) {
continue;
Expand Down Expand Up @@ -2492,7 +2492,8 @@ public function getThisName($offset, $weird_array): string {
return $weird_array[$offset];
}
}',
'error_message' => 'InvalidArrayOffset',
'error_message' => 'MixedArrayAccess',
'ignored_issues' => ['InvalidDocblock'],
],
'unpackTypedIterableWithStringKeysIntoArray' => [
'code' => '<?php
Expand Down
18 changes: 9 additions & 9 deletions tests/KeyOfArrayTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ function getKey() {
'keyOfUnionArrayLiteral' => [
'code' => '<?php
/**
* @return key-of<array<int, string>|array<float, string>>
* @return key-of<array<int, string>|array<string, string>>
*/
function getKey(bool $asFloat) {
if ($asFloat) {
return 42.0;
function getKey(bool $asString) {
if ($asString) {
return "42";
}
return 42;
}
Expand Down Expand Up @@ -194,14 +194,14 @@ public function getKey() {
',
'error_message' => 'InvalidReturnStatement',
],
'noStringAllowedInKeyOfIntFloatArray' => [
'noStringAllowedInKeyOfIntFloatStringArray' => [
'code' => '<?php
/**
* @return key-of<array<int, string>|array<float, string>>
* @return key-of<array<int, string>|array<"42.0", string>>
*/
function getKey(bool $asFloat) {
if ($asFloat) {
return 42.0;
function getKey(bool $asInt) {
if ($asInt) {
return 42;
}
return "42";
}
Expand Down

0 comments on commit 82ff582

Please sign in to comment.