mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Add support for square bracket array declarations is_array checks
This commit is contained in:
parent
d07e340d66
commit
ff578f8468
@ -44,6 +44,8 @@ class StatementsChecker
|
||||
protected static $_check_string_fn = null;
|
||||
protected static $_mock_interfaces = [];
|
||||
|
||||
const TYPE_REGEX = '(\\\?[A-Za-z0-9\<\>\[\]|\\\]+[A-Za-z0-9\<\>\[\]]|\$[a-zA-Z_0-9\<\>\[\]]+)';
|
||||
|
||||
public function __construct(StatementsSource $source, $check_variables = true)
|
||||
{
|
||||
$this->_source = $source;
|
||||
@ -412,6 +414,9 @@ class StatementsChecker
|
||||
$vars_in_scope[$var] = $redefined_vars[$var];
|
||||
}
|
||||
}
|
||||
elseif ($type === '!array' && $redefined_vars[$var] === 'array') {
|
||||
$vars_in_scope[$var] = $redefined_vars[$var];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -504,6 +509,10 @@ class StatementsChecker
|
||||
$var_name = $this->_getVariable($conditional->expr->args[0]->value);
|
||||
$if_types[$var_name] = '!null';
|
||||
}
|
||||
else if (self::_hasArrayCheck($conditional->expr)) {
|
||||
$var_name = $this->_getVariable($conditional->expr->args[0]->value);
|
||||
$if_types[$var_name] = '!array';
|
||||
}
|
||||
}
|
||||
else if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
|
||||
if (self::_hasNullVariable($conditional)) {
|
||||
@ -525,6 +534,10 @@ class StatementsChecker
|
||||
$var_name = $this->_getVariable($conditional->args[0]->value);
|
||||
$if_types[$var_name] = 'null';
|
||||
}
|
||||
else if (self::_hasArrayCheck($conditional)) {
|
||||
$var_name = $this->_getVariable($conditional->args[0]->value);
|
||||
$if_types[$var_name] = 'array';
|
||||
}
|
||||
else if ($conditional instanceof PhpParser\Node\Expr\Empty_) {
|
||||
$var_name = $this->_getVariable($conditional->expr);
|
||||
if ($var_name) {
|
||||
@ -624,6 +637,15 @@ class StatementsChecker
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function _hasArrayCheck(PhpParser\Node\Expr $stmt)
|
||||
{
|
||||
if ($stmt instanceof PhpParser\Node\Expr\FuncCall && $stmt->name instanceof PhpParser\Node\Name && $stmt->name->parts === ['is_array']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function _checkStatic(PhpParser\Node\Stmt\Static_ $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope = [])
|
||||
{
|
||||
foreach ($stmt->vars as $var) {
|
||||
@ -1846,7 +1868,7 @@ class StatementsChecker
|
||||
$return_blocks = explode(' ', $comments['specials']['return'][0]);
|
||||
foreach ($return_blocks as $block) {
|
||||
if ($block) {
|
||||
if ($block && preg_match('/^(\\\?[A-Za-z0-9\<\>|\\\]+[A-Za-z0-9\<\>]|\$[a-zA-Z_0-9\<\>]+)$/', $block)) {
|
||||
if ($block && preg_match('/^' . self::TYPE_REGEX . '$/', $block)) {
|
||||
$return_types = explode('|', $block);
|
||||
break;
|
||||
}
|
||||
@ -1906,6 +1928,10 @@ class StatementsChecker
|
||||
|
||||
protected static function _fixUpLocalReturnType($return_type, $method_id, $namespace, $aliased_classes)
|
||||
{
|
||||
if (strpos($return_type, '[') !== false) {
|
||||
$return_type = self::_convertSquareBrackets($return_type);
|
||||
}
|
||||
|
||||
if ($return_type[0] === '\\') {
|
||||
return $return_type;
|
||||
}
|
||||
@ -1925,6 +1951,10 @@ class StatementsChecker
|
||||
|
||||
protected static function _fixUpReturnType($return_type, $method_id)
|
||||
{
|
||||
if (strpos($return_type, '[') !== false) {
|
||||
$return_type = self::_convertSquareBrackets($return_type);
|
||||
}
|
||||
|
||||
$return_type_parts = [''];
|
||||
$was_char = false;
|
||||
|
||||
@ -1963,6 +1993,25 @@ class StatementsChecker
|
||||
return implode('', $return_type_parts);
|
||||
}
|
||||
|
||||
public function _convertSquareBrackets($type)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/([a-zA-Z\<\>]+)((\[\])+)/',
|
||||
function($matches) {
|
||||
$inner_type = $matches[1];
|
||||
|
||||
$dimensionality = strlen($matches[2]) / 2;
|
||||
|
||||
for ($i = 0; $i < $dimensionality; $i++) {
|
||||
$inner_type = 'array<' . $inner_type . '>';
|
||||
}
|
||||
|
||||
return $inner_type;
|
||||
},
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
public function registerVariable($var_name, $line_number)
|
||||
{
|
||||
if (!isset($this->_all_vars[$var_name])) {
|
||||
@ -2283,7 +2332,7 @@ class StatementsChecker
|
||||
if (isset($comments['specials']['return'])) {
|
||||
$return_blocks = explode(' ', $comments['specials']['return'][0]);
|
||||
foreach ($return_blocks as $block) {
|
||||
if ($block && preg_match('/^(\\\?[A-Za-z0-9\<\>|\\\]+[A-Za-z0-9\<\>]|\$[a-zA-Z_0-9\<\>]+)$/', $block)) {
|
||||
if ($block && preg_match('/^' . self::TYPE_REGEX . '$/', $block)) {
|
||||
$return_types = explode('|', $block);
|
||||
break;
|
||||
}
|
||||
|
@ -723,4 +723,42 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
||||
$file_checker = new \CodeInspector\FileChecker('somefile.php', $stmts);
|
||||
$file_checker->check();
|
||||
}
|
||||
|
||||
public function testArrayUnionTypeAssertion()
|
||||
{
|
||||
$stmts = self::$_parser->parse('<?php
|
||||
/** @var array|null */
|
||||
$ids = (1 + 1 === 2) ? [] : null;
|
||||
|
||||
if ($ids === null) {
|
||||
$ids = [];
|
||||
}
|
||||
|
||||
foreach ($ids as $id) {
|
||||
|
||||
}
|
||||
');
|
||||
|
||||
$file_checker = new \CodeInspector\FileChecker('somefile.php', $stmts);
|
||||
$file_checker->check();
|
||||
}
|
||||
|
||||
public function testArrayUnionTypeAssertionWithIsArray()
|
||||
{
|
||||
$stmts = self::$_parser->parse('<?php
|
||||
/** @var array|null */
|
||||
$ids = (1 + 1 === 2) ? [] : null;
|
||||
|
||||
if (!is_array($ids)) {
|
||||
$ids = [];
|
||||
}
|
||||
|
||||
foreach ($ids as $id) {
|
||||
|
||||
}
|
||||
');
|
||||
|
||||
$file_checker = new \CodeInspector\FileChecker('somefile.php', $stmts);
|
||||
$file_checker->check();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user