1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Allow constants in array offsets to be reasoned about

This commit is contained in:
Matt Brown 2018-05-31 16:49:01 -04:00
parent ef992612d9
commit c31d963918
5 changed files with 75 additions and 14 deletions

View File

@ -416,16 +416,20 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
{
switch (gettype($value)) {
case 'boolean':
return Type::getBool();
if ($value) {
return Type::getTrue();
}
return Type::getFalse();
case 'integer':
return Type::getInt();
return Type::getInt(false, $value);
case 'double':
return Type::getFloat();
return Type::getFloat($value);
case 'string':
return Type::getString();
return Type::getString($value);
case 'array':
return Type::getArray();

View File

@ -54,6 +54,8 @@ class ArrayAssignmentChecker
* @param Context $context
*
* @return false|null
*
* @psalm-suppress UnusedVariable due to Psalm bug
*/
public static function updateArrayType(
StatementsChecker $statements_checker,
@ -131,13 +133,31 @@ class ArrayAssignmentChecker
return null;
}
if ($child_stmt->dim instanceof PhpParser\Node\Scalar\String_) {
if (preg_match('/^(0|[1-9][0-9]*)$/', $child_stmt->dim->value)) {
$var_id_additions[] = '[' . $child_stmt->dim->value . ']';
if ($child_stmt->dim instanceof PhpParser\Node\Scalar\String_
|| ($child_stmt->dim instanceof PhpParser\Node\Expr\ConstFetch
&& $child_stmt->dim->inferredType->isSingleStringLiteral())
) {
if ($child_stmt->dim instanceof PhpParser\Node\Scalar\String_) {
$value = $child_stmt->dim->value;
} else {
$value = $child_stmt->dim->inferredType->getSingleStringLiteral();
}
$var_id_additions[] = '[\'' . $child_stmt->dim->value . '\']';
} elseif ($child_stmt->dim instanceof PhpParser\Node\Scalar\LNumber) {
$var_id_additions[] = '[' . $child_stmt->dim->value . ']';
if (preg_match('/^(0|[1-9][0-9]*)$/', $value)) {
$var_id_additions[] = '[' . $value . ']';
}
$var_id_additions[] = '[\'' . $value . '\']';
} elseif ($child_stmt->dim instanceof PhpParser\Node\Scalar\LNumber
|| ($child_stmt->dim instanceof PhpParser\Node\Expr\ConstFetch
&& $child_stmt->dim->inferredType->isSingleIntLiteral())
) {
if ($child_stmt->dim instanceof PhpParser\Node\Scalar\LNumber) {
$value = $child_stmt->dim->value;
} else {
$value = $child_stmt->dim->inferredType->getSingleIntLiteral();
}
$var_id_additions[] = '[' . $value . ']';
} elseif ($child_stmt->dim instanceof PhpParser\Node\Expr\Variable
&& is_string($child_stmt->dim->name)
) {
@ -199,10 +219,24 @@ class ArrayAssignmentChecker
throw new \InvalidArgumentException('Should never get here');
}
$is_single_string_literal = false;
if ($current_dim instanceof PhpParser\Node\Scalar\String_
|| $current_dim instanceof PhpParser\Node\Scalar\LNumber
|| ($current_dim instanceof PhpParser\Node\Expr\ConstFetch
&& isset($current_dim->inferredType)
&& (($is_single_string_literal = $current_dim->inferredType->isSingleStringLiteral())
|| $current_dim->inferredType->isSingleIntLiteral()))
) {
$key_value = $current_dim->value;
if ($current_dim instanceof PhpParser\Node\Scalar\String_
|| $current_dim instanceof PhpParser\Node\Scalar\LNumber
) {
$key_value = $current_dim->value;
} elseif ($is_single_string_literal) {
$key_value = $current_dim->inferredType->getSingleStringLiteral();
} else {
$key_value = $current_dim->inferredType->getSingleIntLiteral();
}
$has_matching_objectlike_property = false;
@ -261,13 +295,26 @@ class ArrayAssignmentChecker
}
$root_is_string = $root_type->isString();
$is_single_string_literal = false;
if (($current_dim instanceof PhpParser\Node\Scalar\String_
|| $current_dim instanceof PhpParser\Node\Scalar\LNumber)
|| $current_dim instanceof PhpParser\Node\Scalar\LNumber
|| ($current_dim instanceof PhpParser\Node\Expr\ConstFetch
&& isset($current_dim->inferredType)
&& (($is_single_string_literal = $current_dim->inferredType->isSingleStringLiteral())
|| $current_dim->inferredType->isSingleIntLiteral())))
&& ($current_dim instanceof PhpParser\Node\Scalar\String_
|| !$root_is_string)
) {
$key_value = $current_dim->value;
if ($current_dim instanceof PhpParser\Node\Scalar\String_
|| $current_dim instanceof PhpParser\Node\Scalar\LNumber
) {
$key_value = $current_dim->value;
} elseif ($is_single_string_literal) {
$key_value = $current_dim->inferredType->getSingleStringLiteral();
} else {
$key_value = $current_dim->inferredType->getSingleIntLiteral();
}
$has_matching_objectlike_property = false;

View File

@ -728,6 +728,8 @@ class ExpressionChecker
&& is_string($stmt->dim->name)
) {
$offset = '$' . $stmt->dim->name;
} elseif ($stmt->dim instanceof PhpParser\Node\Expr\ConstFetch) {
$offset = implode('\\', $stmt->dim->name->parts);
}
return $root_var_id && $offset !== null ? $root_var_id . '[' . $offset . ']' : null;

View File

@ -1070,7 +1070,8 @@ class StatementsChecker extends SourceChecker implements StatementsSource
$predefined_constants = Config::getInstance()->getPredefinedConstants();
if (isset($predefined_constants[$fq_const_name ?: $const_name])) {
return ClassLikeChecker::getTypeFromValue($predefined_constants[$fq_const_name ?: $const_name]);
$type = ClassLikeChecker::getTypeFromValue($predefined_constants[$fq_const_name ?: $const_name]);
return $type;
}
$stubbed_const_type = $project_checker->codebase->getStubbedConstantType(

View File

@ -925,6 +925,13 @@ class ArrayAssignmentTest extends TestCase
$arr = [1 => 0, 1, 2, 3];
$arr = [1 => "one", 2 => "two", "three")',
],
'constArrayAssignment' => [
'<?php
const BAR = 2;
$arr = [1 => 2];
$arr[BAR] = [6];
$bar = $arr[BAR][0];',
],
];
}