1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-27 04:45:20 +01:00

Transform bad offsets (#5817)

* Transform bad offset access

* fix build
This commit is contained in:
orklah 2021-05-23 20:43:30 +02:00 committed by GitHub
parent d1262b0bec
commit 350df114f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 0 deletions

View File

@ -186,6 +186,8 @@ class ArrayAnalyzer
} elseif ($atomic_key_type instanceof Type\Atomic\TBool) {
$good_types[] = new Type\Atomic\TLiteralInt(0);
$good_types[] = new Type\Atomic\TLiteralInt(1);
} elseif ($atomic_key_type instanceof Type\Atomic\TLiteralFloat) {
$good_types[] = new Type\Atomic\TLiteralInt((int) $atomic_key_type->value);
} elseif ($atomic_key_type instanceof Type\Atomic\TFloat) {
$good_types[] = new Type\Atomic\TInt;
} else {

View File

@ -11,6 +11,7 @@ use Psalm\Internal\DataFlow\DataFlowNode;
use Psalm\Internal\Type\TemplateInferredTypeReplacer;
use Psalm\CodeLocation;
use Psalm\Context;
use Psalm\Internal\Type\TypeCombiner;
use Psalm\Issue\EmptyArrayAccess;
use Psalm\Issue\InvalidArrayAccess;
use Psalm\Issue\InvalidArrayAssignment;
@ -426,6 +427,10 @@ class ArrayFetchAnalyzer
}
}
/**
* @psalm-suppress ComplexMethod to be refactored.
* Good type/bad type behaviour could be mutualised with ArrayAnalyzer
*/
public static function getArrayAccessTypeGivenOffset(
StatementsAnalyzer $statements_analyzer,
PhpParser\Node\Expr\ArrayDimFetch $stmt,
@ -806,6 +811,45 @@ class ArrayFetchAnalyzer
}
}
} else {
$good_types = [];
$bad_types = [];
foreach ($offset_type->getAtomicTypes() as $atomic_key_type) {
if (!$atomic_key_type instanceof Type\Atomic\TString
&& !$atomic_key_type instanceof Type\Atomic\TInt
&& !$atomic_key_type instanceof Type\Atomic\TArrayKey
&& !$atomic_key_type instanceof Type\Atomic\TMixed
&& !$atomic_key_type instanceof Type\Atomic\TTemplateParam
&& !(
$atomic_key_type instanceof Type\Atomic\TObjectWithProperties
&& isset($atomic_key_type->methods['__toString'])
)
) {
$bad_types[] = $atomic_key_type;
if ($atomic_key_type instanceof Type\Atomic\TFalse) {
$good_types[] = new Type\Atomic\TLiteralInt(0);
} elseif ($atomic_key_type instanceof Type\Atomic\TTrue) {
$good_types[] = new Type\Atomic\TLiteralInt(1);
} elseif ($atomic_key_type instanceof Type\Atomic\TBool) {
$good_types[] = new Type\Atomic\TLiteralInt(0);
$good_types[] = new Type\Atomic\TLiteralInt(1);
} elseif ($atomic_key_type instanceof Type\Atomic\TLiteralFloat) {
$good_types[] = new Type\Atomic\TLiteralInt((int)$atomic_key_type->value);
} elseif ($atomic_key_type instanceof Type\Atomic\TFloat) {
$good_types[] = new Type\Atomic\TInt;
} else {
$good_types[] = new Type\Atomic\TArrayKey;
}
}
}
if ($bad_types && $good_types) {
$offset_type->substitute(
TypeCombiner::combine($bad_types, $codebase),
TypeCombiner::combine($good_types, $codebase)
);
}
if (IssueBuffer::accepts(
new InvalidArrayOffset(
'Cannot access value on variable ' . $array_var_id . ' ' . $used_offset

View File

@ -1002,6 +1002,21 @@ class ArrayAccessTest extends TestCase
echo $arr[$s]["c"];
}'
],
'TransformBadOffsetToGoodOnes' => [
'<?php
$index = 1.1;
/** @psalm-suppress InvalidArrayOffset */
$_arr1 = [$index => 5];
$_arr2 = [];
/** @psalm-suppress InvalidArrayOffset */
$_arr2[$index] = 5;',
[
'$_arr1===' => 'non-empty-array<1, 5>',
'$_arr2===' => 'non-empty-array<1, 5>',
]
],
];
}