mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
parent
b9ea115487
commit
9b413cfccc
@ -2021,6 +2021,29 @@ class AssertionFinder
|
||||
}
|
||||
}
|
||||
|
||||
if ($expr->args[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||
&& $expr->args[0]->value->name instanceof PhpParser\Node\Identifier
|
||||
&& $expr->args[0]->value->name->name !== 'class'
|
||||
) {
|
||||
$const_type = null;
|
||||
|
||||
if ($source instanceof StatementsAnalyzer) {
|
||||
$const_type = $source->node_data->getType($expr->args[0]->value);
|
||||
}
|
||||
|
||||
if ($const_type) {
|
||||
if ($const_type->isSingleStringLiteral()) {
|
||||
$first_var_name = $const_type->getSingleStringLiteral()->value;
|
||||
} elseif ($const_type->isSingleIntLiteral()) {
|
||||
$first_var_name = (string) $const_type->getSingleIntLiteral()->value;
|
||||
} else {
|
||||
$first_var_name = null;
|
||||
}
|
||||
} else {
|
||||
$first_var_name = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($first_var_name !== null
|
||||
&& $array_root
|
||||
&& !strpos($first_var_name, '->')
|
||||
|
@ -120,6 +120,8 @@ class NegatedAssertionReconciler extends Reconciler
|
||||
return Type::getEmpty();
|
||||
} elseif (substr($assertion, 0, 9) === 'in-array-') {
|
||||
return $existing_var_type;
|
||||
} elseif (substr($assertion, 0, 14) === 'has-array-key-') {
|
||||
return $existing_var_type;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,13 @@ class SimpleAssertionReconciler extends \Psalm\Type\Reconciler
|
||||
);
|
||||
}
|
||||
|
||||
if (substr($assertion, 0, 14) === 'has-array-key-') {
|
||||
return self::reconcileHasArrayKey(
|
||||
$existing_var_type,
|
||||
substr($assertion, 14)
|
||||
);
|
||||
}
|
||||
|
||||
if ($assertion === 'falsy' || $assertion === 'empty') {
|
||||
return self::reconcileFalsyOrEmpty(
|
||||
$assertion,
|
||||
@ -1263,6 +1270,38 @@ class SimpleAssertionReconciler extends \Psalm\Type\Reconciler
|
||||
return $existing_var_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $suppressed_issues
|
||||
* @param 0|1|2 $failed_reconciliation
|
||||
*/
|
||||
private static function reconcileHasArrayKey(
|
||||
Union $existing_var_type,
|
||||
string $assertion
|
||||
) : Union {
|
||||
foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
|
||||
if ($atomic_type instanceof Type\Atomic\ObjectLike) {
|
||||
$is_class_string = false;
|
||||
|
||||
if (strpos($assertion, '::class')) {
|
||||
list($assertion) = explode('::', $assertion);
|
||||
$is_class_string = true;
|
||||
}
|
||||
|
||||
if (isset($atomic_type->properties[$assertion])) {
|
||||
$atomic_type->properties[$assertion]->possibly_undefined = false;
|
||||
} else {
|
||||
$atomic_type->properties[$assertion] = Type::getMixed();
|
||||
|
||||
if ($is_class_string) {
|
||||
$atomic_type->class_strings[$assertion] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $existing_var_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $suppressed_issues
|
||||
* @param 0|1|2 $failed_reconciliation
|
||||
|
@ -176,6 +176,15 @@ class Reconciler
|
||||
} else {
|
||||
$new_types[$key_parts[2]] = [['=in-array-' . $key_parts[0]]];
|
||||
}
|
||||
|
||||
if ($key_parts[0][0] === '$') {
|
||||
if (isset($new_types[$key_parts[0]])) {
|
||||
$new_types[$key_parts[0]][] = ['=has-array-key-' . $key_parts[2]];
|
||||
} else {
|
||||
$new_types[$key_parts[0]] = [['=has-array-key-' . $key_parts[2]]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -800,6 +800,19 @@ class ConstantTest extends TestCase
|
||||
|
||||
echo B::VALUES["there"];'
|
||||
],
|
||||
'constantArrayKeyExistsWithClassConstant' => [
|
||||
'<?php
|
||||
class Foo {
|
||||
public const F = "key";
|
||||
}
|
||||
|
||||
/** @param array{key?: string} $a */
|
||||
function one(array $a): void {
|
||||
if (array_key_exists(Foo::F, $a)) {
|
||||
echo $a[Foo::F];
|
||||
}
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user