1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Fix #952 - improve checks for string array offsets

This commit is contained in:
Matt Brown 2018-08-21 11:40:29 -04:00
parent bf79169a1d
commit 699a34fc9d
7 changed files with 63 additions and 7 deletions

View File

@ -192,6 +192,7 @@ class MethodCallChecker extends \Psalm\Checker\Statements\Expression\CallChecker
case Type\Atomic\TArray::class:
case Type\Atomic\ObjectLike::class:
case Type\Atomic\TString::class:
case Type\Atomic\TSingleLetter::class:
case Type\Atomic\TLiteralString::class:
case Type\Atomic\TLiteralClassString::class:
case Type\Atomic\TNumericString::class:

View File

@ -38,6 +38,7 @@ use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TSingleLetter;
use Psalm\Type\Atomic\TString;
class ArrayFetchChecker
@ -576,23 +577,37 @@ class ArrayFetchChecker
}
}
if ($type instanceof TSingleLetter) {
$valid_offset_type = Type::getInt(false, 0);
} elseif ($type instanceof TLiteralString) {
$valid_offsets = [];
for ($i = 0, $l = strlen($type->value); $i < $l; $i++) {
$valid_offsets[] = new TLiteralInt($i);
}
$valid_offset_type = new Type\Union($valid_offsets);
} else {
$valid_offset_type = Type::getInt();
}
if (!TypeChecker::isContainedBy(
$project_checker->codebase,
$offset_type,
Type::getInt(),
$valid_offset_type,
true
)) {
$expected_offset_types[] = 'int';
$expected_offset_types[] = $valid_offset_type->getId();
} else {
$has_valid_offset = true;
}
if (!$array_access_type) {
$array_access_type = Type::getString();
$array_access_type = Type::getSingleLetter();
} else {
$array_access_type = Type::combineUnionTypes(
$array_access_type,
Type::getString()
Type::getSingleLetter()
);
}

View File

@ -28,6 +28,7 @@ use Psalm\Type\Atomic\TNumericString;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TResource;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TSingleLetter;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTrue;
@ -604,7 +605,10 @@ class TypeChecker
return true;
}
if (get_class($container_type_part) === TString::class && $input_type_part instanceof TLiteralString) {
if ((get_class($container_type_part) === TString::class
|| get_class($container_type_part) === TSingleLetter::class)
&& $input_type_part instanceof TLiteralString
) {
return true;
}
@ -622,7 +626,9 @@ class TypeChecker
return false;
}
if (get_class($input_type_part) === TString::class && $container_type_part instanceof TLiteralString) {
if ((get_class($input_type_part) === TString::class || get_class($container_type_part) === TSingleLetter::class)
&& $container_type_part instanceof TLiteralString
) {
$type_coerced = true;
$type_coerced_from_scalar = true;
@ -651,6 +657,7 @@ class TypeChecker
if (($input_type_part instanceof TClassString || $input_type_part instanceof TLiteralClassString)
&& (get_class($container_type_part) === TString::class
|| get_class($container_type_part) === TSingleLetter::class
|| get_class($container_type_part) === Type\Atomic\GetClassT::class)
) {
return true;

View File

@ -24,6 +24,7 @@ use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TResource;
use Psalm\Type\Atomic\TSingleLetter;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTrue;
use Psalm\Type\Atomic\TVoid;
@ -742,6 +743,16 @@ abstract class Type
return new Union([$type]);
}
/**
* @return Type\Union
*/
public static function getSingleLetter()
{
$type = new TSingleLetter;
return new Union([$type]);
}
/**
* @param string $class_type
*

View File

@ -0,0 +1,6 @@
<?php
namespace Psalm\Type\Atomic;
class TSingleLetter extends TString
{
}

View File

@ -373,7 +373,16 @@ class TypeCombination
}
} else {
$combination->strings = null;
$combination->value_types[$type_key] = $type;
if (!isset($combination->value_types['string'])) {
$combination->value_types[$type_key] = $type;
} elseif (get_class($combination->value_types['string']) !== TString::class) {
if (get_class($type) === TString::class) {
$combination->value_types[$type_key] = $type;
} elseif (get_class($combination->value_types['string']) !== get_class($type)) {
$combination->value_types[$type_key] = new TString();
}
}
}
} elseif ($type instanceof TInt) {
if ($type instanceof TLiteralInt) {

View File

@ -243,6 +243,13 @@ class ArrayAccessTest extends TestCase
echo $x["a"];',
'error_message' => 'InvalidArrayOffset',
],
'noImpossibleStringAccess' => [
'<?php
function foo(string $s) : void {
echo $s[0][1];
}',
'error_message' => 'InvalidArrayOffset',
],
];
}
}