mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Merge pull request #7449 from orklah/7415-2
Array key exists assert both ways
This commit is contained in:
commit
f6369dc086
@ -3698,65 +3698,65 @@ class AssertionFinder
|
||||
|
||||
if ($literal_assertions && $first_var_name) {
|
||||
$if_types[$first_var_name] = [$literal_assertions];
|
||||
} else {
|
||||
$array_root = isset($expr->getArgs()[1]->value)
|
||||
? ExpressionIdentifier::getArrayVarId(
|
||||
$expr->getArgs()[1]->value,
|
||||
$this_class_name,
|
||||
$source
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
if ($array_root) {
|
||||
if ($first_var_name === null && isset($expr->getArgs()[0])) {
|
||||
$first_arg = $expr->getArgs()[0];
|
||||
$array_root = isset($expr->getArgs()[1]->value)
|
||||
? ExpressionIdentifier::getArrayVarId(
|
||||
$expr->getArgs()[1]->value,
|
||||
$this_class_name,
|
||||
$source
|
||||
)
|
||||
: null;
|
||||
|
||||
if ($first_arg->value instanceof PhpParser\Node\Scalar\String_) {
|
||||
$first_var_name = '\'' . $first_arg->value->value . '\'';
|
||||
} elseif ($first_arg->value instanceof PhpParser\Node\Scalar\LNumber) {
|
||||
$first_var_name = (string)$first_arg->value->value;
|
||||
}
|
||||
if ($array_root) {
|
||||
if ($first_var_name === null && isset($expr->getArgs()[0])) {
|
||||
$first_arg = $expr->getArgs()[0];
|
||||
|
||||
if ($first_arg->value instanceof PhpParser\Node\Scalar\String_) {
|
||||
$first_var_name = '\'' . $first_arg->value->value . '\'';
|
||||
} elseif ($first_arg->value instanceof PhpParser\Node\Scalar\LNumber) {
|
||||
$first_var_name = (string)$first_arg->value->value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||
&& $expr->getArgs()[0]->value->name instanceof PhpParser\Node\Identifier
|
||||
&& $expr->getArgs()[0]->value->name->name !== 'class'
|
||||
) {
|
||||
$const_type = null;
|
||||
|
||||
if ($source instanceof StatementsAnalyzer) {
|
||||
$const_type = $source->node_data->getType($expr->getArgs()[0]->value);
|
||||
}
|
||||
|
||||
if ($expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||
&& $expr->getArgs()[0]->value->name instanceof PhpParser\Node\Identifier
|
||||
&& $expr->getArgs()[0]->value->name->name !== 'class'
|
||||
) {
|
||||
$const_type = null;
|
||||
|
||||
if ($source instanceof StatementsAnalyzer) {
|
||||
$const_type = $source->node_data->getType($expr->getArgs()[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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
} elseif ($expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\Variable
|
||||
&& $source instanceof StatementsAnalyzer
|
||||
&& ($first_var_type = $source->node_data->getType($expr->getArgs()[0]->value))
|
||||
) {
|
||||
foreach ($first_var_type->getLiteralStrings() as $array_literal_type) {
|
||||
$if_types[$array_root . "['" . $array_literal_type->value . "']"] = [[new ArrayKeyExists()]];
|
||||
}
|
||||
foreach ($first_var_type->getLiteralInts() as $array_literal_type) {
|
||||
$if_types[$array_root . "[" . $array_literal_type->value . "]"] = [[new ArrayKeyExists()]];
|
||||
}
|
||||
} else {
|
||||
$first_var_name = null;
|
||||
}
|
||||
} elseif ($expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\Variable
|
||||
&& $source instanceof StatementsAnalyzer
|
||||
&& ($first_var_type = $source->node_data->getType($expr->getArgs()[0]->value))
|
||||
) {
|
||||
foreach ($first_var_type->getLiteralStrings() as $array_literal_type) {
|
||||
$if_types[$array_root . "['" . $array_literal_type->value . "']"] = [[new ArrayKeyExists()]];
|
||||
}
|
||||
foreach ($first_var_type->getLiteralInts() as $array_literal_type) {
|
||||
$if_types[$array_root . "[" . $array_literal_type->value . "]"] = [[new ArrayKeyExists()]];
|
||||
}
|
||||
}
|
||||
|
||||
if ($first_var_name !== null
|
||||
&& !strpos($first_var_name, '->')
|
||||
&& !strpos($first_var_name, '[')
|
||||
) {
|
||||
$if_types[$array_root . '[' . $first_var_name . ']'] = [[new ArrayKeyExists()]];
|
||||
}
|
||||
if ($first_var_name !== null
|
||||
&& !strpos($first_var_name, '->')
|
||||
&& !strpos($first_var_name, '[')
|
||||
) {
|
||||
$if_types[$array_root . '[' . $first_var_name . ']'] = [[new ArrayKeyExists()]];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ use const PHP_EOL;
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Contains methods that aid in the scanning of Psalm's codebase>
|
||||
* Contains methods that aid in the scanning of Psalm's codebase
|
||||
*/
|
||||
class Scanner
|
||||
{
|
||||
|
@ -378,6 +378,35 @@ class ArrayKeyExistsTest extends TestCase
|
||||
return true;
|
||||
}'
|
||||
],
|
||||
'arrayKeyExistsAssertBothWays' => [
|
||||
'code' => '<?php
|
||||
class a {
|
||||
const STATE_A = 0;
|
||||
const STATE_B = 1;
|
||||
const STATE_C = 2;
|
||||
/**
|
||||
* @return array<self::STATE_*, non-empty-string>
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function getStateLabels(): array {
|
||||
return [
|
||||
self::STATE_A => "A",
|
||||
self::STATE_B => "B",
|
||||
self::STATE_C => "C",
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @param self::STATE_* $state
|
||||
*/
|
||||
public static function getStateLabelIf(int $state): string {
|
||||
$states = self::getStateLabels();
|
||||
if (array_key_exists($state, $states)) {
|
||||
return $states[$state];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user