1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Handle variable paths in a less naive fashion

Fixes #564
This commit is contained in:
Matt Brown 2018-03-08 15:57:46 -05:00
parent 19ee843d6c
commit b54fb06552
3 changed files with 87 additions and 2 deletions

View File

@ -12,6 +12,9 @@ use Psalm\IssueBuffer;
class AlgebraChecker
{
/** @var array<string, array<int, string>> */
private static $broken_paths = [];
/**
* @param PhpParser\Node\Expr $conditional
* @param string|null $this_class_name
@ -88,7 +91,7 @@ class AlgebraChecker
foreach ($assertions as $var => $type) {
if ($type === 'isset' || $type === '!empty') {
$key_parts = preg_split('/(->|\[|\])/', $var, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$key_parts = self::breakUpPathIntoParts($var);
$base_key = array_shift($key_parts);
@ -144,6 +147,83 @@ class AlgebraChecker
return [new Clause([], true)];
}
/**
* @param string $path
*
* @return array<int, string>
*/
public static function breakUpPathIntoParts($path)
{
if (isset(self::$broken_paths[$path])) {
return self::$broken_paths[$path];
}
$chars = str_split($path);
$string_char = null;
$escape_char = false;
$parts = [''];
$parts_offset = 0;
for ($i = 0, $char_count = count($chars); $i < $char_count; ++$i) {
$char = $chars[$i];
if ($string_char) {
if ($char === $string_char && !$escape_char) {
$string_char = null;
}
if ($char === '\\') {
$escape_char = !$escape_char;
}
$parts[$parts_offset] .= $char;
continue;
}
switch ($char) {
case '[':
case ']':
$parts_offset++;
$parts[$parts_offset] = $char;
$parts_offset++;
continue;
case '\'':
case '"':
if (!isset($parts[$parts_offset])) {
$parts[$parts_offset] = '';
}
$parts[$parts_offset] .= $char;
$string_char = $char;
continue;
case '-':
if ($i < $char_count - 1 && $chars[$i + 1] === '>') {
++$i;
$parts_offset++;
$parts[$parts_offset] = '->';
$parts_offset++;
continue;
}
// fall through
default:
if (!isset($parts[$parts_offset])) {
$parts[$parts_offset] = '';
}
$parts[$parts_offset] .= $char;
}
}
self::$broken_paths[$path] = $parts;
return $parts;
}
/**
* Negates a set of clauses
* negateClauses([$a || $b]) => !$a && !$b

View File

@ -1,6 +1,7 @@
<?php
namespace Psalm\Type;
use Psalm\Checker\AlgebraChecker;
use Psalm\Checker\ProjectChecker;
use Psalm\Checker\StatementsChecker;
use Psalm\Checker\TraitChecker;
@ -913,7 +914,7 @@ class Reconciler
*/
private static function getValueForKey(ProjectChecker $project_checker, $key, array &$existing_keys)
{
$key_parts = preg_split('/(->|\[|\])/', $key, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$key_parts = AlgebraChecker::breakUpPathIntoParts($key);
if (count($key_parts) === 1) {
return isset($existing_keys[$key_parts[0]]) ? clone $existing_keys[$key_parts[0]] : null;

View File

@ -177,6 +177,10 @@ class IssetTest extends TestCase
return $arr[$b];
}',
],
'noExceptionOnBracketString' => [
'<?php
if (isset($foo["bar[]"])) {}',
],
];
}