mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +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 $_check_string_fn = null;
|
||||||
protected static $_mock_interfaces = [];
|
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)
|
public function __construct(StatementsSource $source, $check_variables = true)
|
||||||
{
|
{
|
||||||
$this->_source = $source;
|
$this->_source = $source;
|
||||||
@ -412,6 +414,9 @@ class StatementsChecker
|
|||||||
$vars_in_scope[$var] = $redefined_vars[$var];
|
$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);
|
$var_name = $this->_getVariable($conditional->expr->args[0]->value);
|
||||||
$if_types[$var_name] = '!null';
|
$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) {
|
else if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
|
||||||
if (self::_hasNullVariable($conditional)) {
|
if (self::_hasNullVariable($conditional)) {
|
||||||
@ -525,6 +534,10 @@ class StatementsChecker
|
|||||||
$var_name = $this->_getVariable($conditional->args[0]->value);
|
$var_name = $this->_getVariable($conditional->args[0]->value);
|
||||||
$if_types[$var_name] = 'null';
|
$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_) {
|
else if ($conditional instanceof PhpParser\Node\Expr\Empty_) {
|
||||||
$var_name = $this->_getVariable($conditional->expr);
|
$var_name = $this->_getVariable($conditional->expr);
|
||||||
if ($var_name) {
|
if ($var_name) {
|
||||||
@ -624,6 +637,15 @@ class StatementsChecker
|
|||||||
return false;
|
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 = [])
|
protected function _checkStatic(PhpParser\Node\Stmt\Static_ $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope = [])
|
||||||
{
|
{
|
||||||
foreach ($stmt->vars as $var) {
|
foreach ($stmt->vars as $var) {
|
||||||
@ -1846,7 +1868,7 @@ class StatementsChecker
|
|||||||
$return_blocks = explode(' ', $comments['specials']['return'][0]);
|
$return_blocks = explode(' ', $comments['specials']['return'][0]);
|
||||||
foreach ($return_blocks as $block) {
|
foreach ($return_blocks as $block) {
|
||||||
if ($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);
|
$return_types = explode('|', $block);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1906,6 +1928,10 @@ class StatementsChecker
|
|||||||
|
|
||||||
protected static function _fixUpLocalReturnType($return_type, $method_id, $namespace, $aliased_classes)
|
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] === '\\') {
|
if ($return_type[0] === '\\') {
|
||||||
return $return_type;
|
return $return_type;
|
||||||
}
|
}
|
||||||
@ -1925,6 +1951,10 @@ class StatementsChecker
|
|||||||
|
|
||||||
protected static function _fixUpReturnType($return_type, $method_id)
|
protected static function _fixUpReturnType($return_type, $method_id)
|
||||||
{
|
{
|
||||||
|
if (strpos($return_type, '[') !== false) {
|
||||||
|
$return_type = self::_convertSquareBrackets($return_type);
|
||||||
|
}
|
||||||
|
|
||||||
$return_type_parts = [''];
|
$return_type_parts = [''];
|
||||||
$was_char = false;
|
$was_char = false;
|
||||||
|
|
||||||
@ -1963,6 +1993,25 @@ class StatementsChecker
|
|||||||
return implode('', $return_type_parts);
|
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)
|
public function registerVariable($var_name, $line_number)
|
||||||
{
|
{
|
||||||
if (!isset($this->_all_vars[$var_name])) {
|
if (!isset($this->_all_vars[$var_name])) {
|
||||||
@ -2283,7 +2332,7 @@ class StatementsChecker
|
|||||||
if (isset($comments['specials']['return'])) {
|
if (isset($comments['specials']['return'])) {
|
||||||
$return_blocks = explode(' ', $comments['specials']['return'][0]);
|
$return_blocks = explode(' ', $comments['specials']['return'][0]);
|
||||||
foreach ($return_blocks as $block) {
|
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);
|
$return_types = explode('|', $block);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -723,4 +723,42 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
$file_checker = new \CodeInspector\FileChecker('somefile.php', $stmts);
|
$file_checker = new \CodeInspector\FileChecker('somefile.php', $stmts);
|
||||||
$file_checker->check();
|
$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