1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 12:24:49 +01:00

Fix #285 - add PossiblyInvalidArrayAccess

This commit is contained in:
Matthew Brown 2017-11-11 21:22:11 -05:00
parent 4f2a200b45
commit a0a9128375
6 changed files with 42 additions and 7 deletions

View File

@ -156,6 +156,7 @@
<xs:element name="PossiblyFalseArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyFalseReference" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidArrayAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyNullArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyNullArrayAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyNullFunctionCall" type="IssueHandlerType" minOccurs="0" />

View File

@ -187,7 +187,7 @@ class CommentChecker
&& preg_match('/^(\.\.\.)?&?\$[A-Za-z0-9_]+,?$/', $line_parts[1])
&& !strpos($line_parts[0], '::')
&& $line_parts[0][0] !== '{'
&& !in_array($line_parts[0], ['null', 'false', 'true'])
&& !in_array($line_parts[0], ['null', 'false', 'true'], true)
) {
if ($line_parts[1][0] === '&') {
$line_parts[1] = substr($line_parts[1], 1);
@ -199,8 +199,6 @@ class CommentChecker
$line_parts[1] = preg_replace('/,$/', '', $line_parts[1]);
$info->params[] = [
'name' => $line_parts[1],
'type' => $line_parts[0],

View File

@ -26,6 +26,7 @@ use Psalm\Issue\NullArrayAccess;
use Psalm\Issue\NullPropertyFetch;
use Psalm\Issue\NullReference;
use Psalm\Issue\ParentNotFound;
use Psalm\Issue\PossiblyInvalidArrayAccess;
use Psalm\Issue\PossiblyNullArrayAccess;
use Psalm\Issue\PossiblyNullPropertyFetch;
use Psalm\Issue\PossiblyNullReference;
@ -907,6 +908,9 @@ class FetchChecker
return;
}
$has_array_access = false;
$non_array_types = [];
foreach ($var_type->types as &$type) {
if ($type instanceof TNull) {
if (IssueBuffer::accepts(
@ -931,6 +935,7 @@ class FetchChecker
if ($type instanceof Type\Atomic\TArray || $type instanceof Type\Atomic\ObjectLike) {
$value_index = null;
$has_array_access = true;
if ($type instanceof Type\Atomic\TArray) {
// create a union type to pass back to the statement
@ -1174,17 +1179,36 @@ class FetchChecker
!ClassChecker::classImplements($project_checker, $type->value, 'ArrayAccess')
)
) {
$non_array_types[] = $var_type;
}
}
if ($non_array_types) {
if ($has_array_access) {
if (IssueBuffer::accepts(
new PossiblyInvalidArrayAccess(
'Cannot access array value on non-array variable ' .
$array_var_id . ' of type ' . $non_array_types[0],
new CodeLocation($statements_checker->getSource(), $stmt)
),
$statements_checker->getSuppressedIssues()
)
) {
// do nothing
}
} else {
if (IssueBuffer::accepts(
new InvalidArrayAccess(
'Cannot access array value on non-array variable ' .
$array_var_id . ' of type ' . $var_type,
$array_var_id . ' of type ' . $non_array_types[0],
new CodeLocation($statements_checker->getSource(), $stmt)
),
$statements_checker->getSuppressedIssues()
)) {
$stmt->inferredType = Type::getMixed();
break;
// fall through
}
$stmt->inferredType = Type::getMixed();
}
}
}

View File

@ -0,0 +1,6 @@
<?php
namespace Psalm\Issue;
class PossiblyInvalidArrayAccess extends CodeError
{
}

View File

@ -100,6 +100,12 @@ class ArrayAccessTest extends TestCase
echo $a[0];',
'error_message' => 'InvalidArrayAccess',
],
'possiblyInvalidArrayAccess' => [
'<?php
$a = rand(0, 10) > 5 ? 5 : ["hello"];
echo $a[0];',
'error_message' => 'PossiblyInvalidArrayAccess',
],
'mixedArrayAccess' => [
'<?php
/** @var mixed */

View File

@ -266,7 +266,7 @@ class FunctionCallTest extends TestCase
foo($a3);',
'assertions' => [
'$a3' => 'array{bye:int, hi:int}'
'$a3' => 'array{bye:int, hi:int}',
],
],
'goodByRef' => [