Skip to content

Commit

Permalink
Transform bad offsets (#5817)
Browse files Browse the repository at this point in the history
* Transform bad offset access

* fix build
  • Loading branch information
orklah authored May 23, 2021
1 parent d1262b0 commit 350df11
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ public static function analyze(
} 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
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;
Expand Down Expand Up @@ -426,6 +427,10 @@ public static function taintArrayFetch(
}
}

/**
* @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,
Expand Down Expand Up @@ -806,6 +811,45 @@ public static function getArrayAccessTypeGivenOffset(
}
}
} 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
Expand Down
15 changes: 15 additions & 0 deletions tests/ArrayAccessTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,21 @@ function foo(?array $arr, string $s) : void {
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>',
]
],
];
}

Expand Down

0 comments on commit 350df11

Please sign in to comment.