mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Improve reconciliation of arrays with constant offsets
This commit is contained in:
parent
6d02aa86e8
commit
2469f04715
@ -503,35 +503,6 @@ class Context
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This removes all clauses that are invalidated because
|
||||
* all possibilities are overwritten by changed var ids
|
||||
*
|
||||
* @param Clause[] $clauses
|
||||
* @param array<string, bool> $changed_var_ids
|
||||
*
|
||||
* @return list<Clause>
|
||||
*/
|
||||
public static function removeOverwrittenClauses(array $clauses, array $changed_var_ids)
|
||||
{
|
||||
$included_clauses = [];
|
||||
|
||||
foreach ($clauses as $c) {
|
||||
if ($c->wedge) {
|
||||
$included_clauses[] = $c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!\array_diff_key($c->possibilities, $changed_var_ids)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$included_clauses[] = $c;
|
||||
}
|
||||
|
||||
return $included_clauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Clause[] $clauses
|
||||
* @param array<string, bool> $changed_var_ids
|
||||
|
@ -142,13 +142,6 @@ class IfAnalyzer
|
||||
|
||||
$entry_clauses = $context->clauses;
|
||||
|
||||
if ($if_scope->if_cond_changed_var_ids) {
|
||||
$entry_clauses = Context::removeOverwrittenClauses(
|
||||
$entry_clauses,
|
||||
$if_scope->if_cond_changed_var_ids
|
||||
);
|
||||
}
|
||||
|
||||
// this will see whether any of the clauses in set A conflict with the clauses in set B
|
||||
AlgebraAnalyzer::checkForParadox(
|
||||
$context->clauses,
|
||||
@ -1085,13 +1078,6 @@ class IfAnalyzer
|
||||
$cond_assigned_var_ids
|
||||
);
|
||||
|
||||
if ($if_scope->if_cond_changed_var_ids) {
|
||||
$entry_clauses = Context::removeOverwrittenClauses(
|
||||
$entry_clauses,
|
||||
$if_scope->if_cond_changed_var_ids
|
||||
);
|
||||
}
|
||||
|
||||
$elseif_context_clauses = array_merge($entry_clauses, $elseif_clauses);
|
||||
|
||||
if ($elseif_context->reconciled_expression_clauses) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
namespace Psalm\Internal\Analyzer\Statements\Expression\Assignment;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
@ -223,6 +224,15 @@ class ArrayAssignmentAnalyzer
|
||||
if ($object_id) {
|
||||
$var_id_additions[] = '[' . $object_id . '->' . $child_stmt->dim->name->name . ']';
|
||||
}
|
||||
} elseif ($child_stmt->dim instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||
&& $child_stmt->dim->name instanceof PhpParser\Node\Identifier
|
||||
&& $child_stmt->dim->class instanceof PhpParser\Node\Name
|
||||
) {
|
||||
$object_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
|
||||
$child_stmt->dim->class,
|
||||
$statements_analyzer->getAliases()
|
||||
);
|
||||
$var_id_additions[] = '[' . $object_name . '::' . $child_stmt->dim->name->name . ']';
|
||||
} else {
|
||||
$var_id_additions[] = '[' . $child_stmt_dim_type . ']';
|
||||
$full_var_id = false;
|
||||
|
@ -105,6 +105,11 @@ class Reconciler
|
||||
|
||||
$base_key = array_shift($key_parts);
|
||||
|
||||
if ($base_key[0] !== '$' && count($key_parts) > 2 && $key_parts[0] === '::$') {
|
||||
$base_key .= array_shift($key_parts);
|
||||
$base_key .= array_shift($key_parts);
|
||||
}
|
||||
|
||||
if (!isset($existing_types[$base_key]) || $existing_types[$base_key]->isNullable()) {
|
||||
if (!isset($new_types[$base_key])) {
|
||||
$new_types[$base_key] = [['=isset']];
|
||||
@ -465,6 +470,11 @@ class Reconciler
|
||||
|
||||
$base_key = array_shift($key_parts);
|
||||
|
||||
if ($base_key[0] !== '$' && count($key_parts) > 2 && $key_parts[0] === '::$') {
|
||||
$base_key .= array_shift($key_parts);
|
||||
$base_key .= array_shift($key_parts);
|
||||
}
|
||||
|
||||
if (!isset($existing_keys[$base_key])) {
|
||||
if (strpos($base_key, '::')) {
|
||||
list($fq_class_name, $const_name) = explode('::', $base_key);
|
||||
|
@ -2238,7 +2238,39 @@ class ConditionalTest extends \Psalm\Tests\TestCase
|
||||
echo isset($a);
|
||||
}'
|
||||
],
|
||||
'assertOnStaticClassKey' => [
|
||||
'assertOnVarStaticClassKey' => [
|
||||
'<?php
|
||||
abstract class Obj {
|
||||
/**
|
||||
* @param array<class-string, array<string, int>> $arr
|
||||
* @return array<string, int>
|
||||
*/
|
||||
public static function getArr(array $arr) : array {
|
||||
if (!isset($arr[static::class])) {
|
||||
$arr[static::class] = ["hello" => 5];
|
||||
}
|
||||
|
||||
return $arr[static::class];
|
||||
}
|
||||
}'
|
||||
],
|
||||
'assertOnVarVar' => [
|
||||
'<?php
|
||||
abstract class Obj {
|
||||
/**
|
||||
* @param array<class-string, array<string, int>> $arr
|
||||
* @return array<string, int>
|
||||
*/
|
||||
function getArr(array $arr, string $s) : array {
|
||||
if (!isset($arr[$s])) {
|
||||
$arr[$s] = ["hello" => 5];
|
||||
}
|
||||
|
||||
return $arr[$s];
|
||||
}
|
||||
}'
|
||||
],
|
||||
'assertOnPropertyStaticClassKey' => [
|
||||
'<?php
|
||||
abstract class Obj {
|
||||
/** @var array<class-string, array<string, int>> */
|
||||
@ -2246,11 +2278,12 @@ class ConditionalTest extends \Psalm\Tests\TestCase
|
||||
|
||||
/** @return array<string, int> */
|
||||
public static function getArr() : array {
|
||||
if (!isset(self::$arr[static::class])) {
|
||||
self::$arr[static::class] = ["hello" => 5];
|
||||
$arr = self::$arr;
|
||||
if (!isset($arr[static::class])) {
|
||||
$arr[static::class] = ["hello" => 5];
|
||||
}
|
||||
|
||||
return self::$arr[static::class];
|
||||
return $arr[static::class];
|
||||
}
|
||||
}'
|
||||
],
|
||||
@ -2287,6 +2320,38 @@ class ConditionalTest extends \Psalm\Tests\TestCase
|
||||
}
|
||||
}'
|
||||
],
|
||||
'reconcileEmptinessBetter' => [
|
||||
'<?php
|
||||
/**
|
||||
* @param string|array $valuePath
|
||||
*/
|
||||
function combine($valuePath) : void {
|
||||
if (!empty($valuePath) && is_array($valuePath)) {
|
||||
|
||||
} elseif (!empty($valuePath)) {
|
||||
echo $valuePath;
|
||||
}
|
||||
}',
|
||||
],
|
||||
'issetAssertionOnStaticProperty' => [
|
||||
'<?php
|
||||
class C {
|
||||
protected static array $cache = [];
|
||||
|
||||
/**
|
||||
* @psalm-suppress MixedArrayAccess
|
||||
* @psalm-suppress MixedReturnStatement
|
||||
* @psalm-suppress MixedInferredReturnType
|
||||
*/
|
||||
public static function get(string $k1, string $k2) : ?string {
|
||||
if (!isset(static::$cache[$k1][$k2])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::$cache[$k1][$k2];
|
||||
}
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user