mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Merge pull request #6445 from TysonAndre/negate-in_array-v2
Fix false positive ParadoxicalCondition in negation of in_array
This commit is contained in:
commit
994c927b6a
@ -12,6 +12,7 @@ use Psalm\IssueBuffer;
|
|||||||
use function array_intersect_key;
|
use function array_intersect_key;
|
||||||
use function array_unique;
|
use function array_unique;
|
||||||
use function count;
|
use function count;
|
||||||
|
use function preg_match;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -136,6 +137,12 @@ class AlgebraAnalyzer
|
|||||||
$negated_clause_2_contains_1_possibilities = false;
|
$negated_clause_2_contains_1_possibilities = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
foreach ($keyed_possibilities as $possibility) {
|
||||||
|
if (preg_match('@^!?in-array-@', $possibility)) {
|
||||||
|
$negated_clause_2_contains_1_possibilities = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($negated_clause_2_contains_1_possibilities) {
|
if ($negated_clause_2_contains_1_possibilities) {
|
||||||
|
@ -467,7 +467,7 @@ class AssertionFinder
|
|||||||
|
|
||||||
if ($var_name_left &&
|
if ($var_name_left &&
|
||||||
(!$var_type->isSingle() || $var_type->getAssertionString() !== $assertion)) {
|
(!$var_type->isSingle() || $var_type->getAssertionString() !== $assertion)) {
|
||||||
$if_types[$var_name_left] = [['~'.$assertion]];
|
$if_types[$var_name_left] = [['='.$assertion]];
|
||||||
}
|
}
|
||||||
|
|
||||||
$var_name_right = ExpressionIdentifier::getArrayVarId(
|
$var_name_right = ExpressionIdentifier::getArrayVarId(
|
||||||
@ -478,7 +478,7 @@ class AssertionFinder
|
|||||||
|
|
||||||
if ($var_name_right &&
|
if ($var_name_right &&
|
||||||
(!$other_type->isSingle() || $other_type->getAssertionString() !== $assertion)) {
|
(!$other_type->isSingle() || $other_type->getAssertionString() !== $assertion)) {
|
||||||
$if_types[$var_name_right] = [['~'.$assertion]];
|
$if_types[$var_name_right] = [['='.$assertion]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $if_types ? [$if_types] : [];
|
return $if_types ? [$if_types] : [];
|
||||||
@ -3531,6 +3531,15 @@ class AssertionFinder
|
|||||||
$assertions = [];
|
$assertions = [];
|
||||||
|
|
||||||
if (!$is_sealed) {
|
if (!$is_sealed) {
|
||||||
|
// `in-array-*` has special handling in the detection of paradoxical
|
||||||
|
// conditions and the fact the negation doesn't imply anything.
|
||||||
|
//
|
||||||
|
// In the vast majority of cases, the negation of `in-array-*`
|
||||||
|
// (`Algebra::negateType`) doesn't imply anything because:
|
||||||
|
// - The array can be empty, or
|
||||||
|
// - The array may have one of the types but not the others.
|
||||||
|
//
|
||||||
|
// NOTE: the negation of the negation is the original assertion.
|
||||||
if ($value_type->getId() !== '' && !$value_type->isMixed()) {
|
if ($value_type->getId() !== '' && !$value_type->isMixed()) {
|
||||||
$assertions[] = 'in-array-' . $value_type->getId();
|
$assertions[] = 'in-array-' . $value_type->getId();
|
||||||
}
|
}
|
||||||
@ -3542,8 +3551,16 @@ class AssertionFinder
|
|||||||
|| $atomic_value_type instanceof Type\Atomic\TEnumCase
|
|| $atomic_value_type instanceof Type\Atomic\TEnumCase
|
||||||
) {
|
) {
|
||||||
$assertions[] = '=' . $atomic_value_type->getAssertionString();
|
$assertions[] = '=' . $atomic_value_type->getAssertionString();
|
||||||
} else {
|
} elseif ($atomic_value_type instanceof Type\Atomic\TFalse
|
||||||
|
|| $atomic_value_type instanceof Type\Atomic\TTrue
|
||||||
|
|| $atomic_value_type instanceof Type\Atomic\TNull
|
||||||
|
) {
|
||||||
$assertions[] = $atomic_value_type->getAssertionString();
|
$assertions[] = $atomic_value_type->getAssertionString();
|
||||||
|
} elseif (!$atomic_value_type instanceof Type\Atomic\TMixed) {
|
||||||
|
// mixed doesn't tell us anything and can be omitted.
|
||||||
|
//
|
||||||
|
// For the meaning of in-array, see the above comment.
|
||||||
|
$assertions[] = 'in-array-' . $value_type->getId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,6 +195,34 @@ class InArrayTest extends \Psalm\Tests\TestCase
|
|||||||
[],
|
[],
|
||||||
'8.0'
|
'8.0'
|
||||||
],
|
],
|
||||||
|
'in_array-string-twice' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @param string[] $list1
|
||||||
|
* @param string[] $list2
|
||||||
|
*/
|
||||||
|
function contains(array $list1, array $list2, string $element): void
|
||||||
|
{
|
||||||
|
if (in_array($element, $list1, true)) {
|
||||||
|
} elseif (in_array($element, $list2, true)) {
|
||||||
|
}
|
||||||
|
}',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
'8.0'
|
||||||
|
],
|
||||||
|
'in_array-keyed-array-string-twice' => [
|
||||||
|
'<?php
|
||||||
|
function contains(string $a, string $b, mixed $element): void
|
||||||
|
{
|
||||||
|
if (in_array($element, [$a], true)) {
|
||||||
|
} elseif (in_array($element, [$b], true)) {
|
||||||
|
}
|
||||||
|
}',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
'8.0'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,6 +392,28 @@ class InArrayTest extends \Psalm\Tests\TestCase
|
|||||||
'error_message' => 'MixedReturnStatement - src' . DIRECTORY_SEPARATOR . 'somefile.php:12:32 - Could not infer a return type',
|
'error_message' => 'MixedReturnStatement - src' . DIRECTORY_SEPARATOR . 'somefile.php:12:32 - Could not infer a return type',
|
||||||
'error_level' => ['RedundantConditionGivenDocblockType'],
|
'error_level' => ['RedundantConditionGivenDocblockType'],
|
||||||
],
|
],
|
||||||
|
'inArrayDetectType' => [
|
||||||
|
'<?php
|
||||||
|
function x($foo, string $bar): void {
|
||||||
|
if (!in_array($foo, [$bar], true)) {
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($foo)) {}
|
||||||
|
}',
|
||||||
|
// foo is always string
|
||||||
|
'error_message' => 'RedundantCondition',
|
||||||
|
],
|
||||||
|
'inArrayRemoveInvalid' => [
|
||||||
|
'<?php
|
||||||
|
function x(?string $foo, int $bar): void {
|
||||||
|
if (!in_array($foo, [$bar], true)) {
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}',
|
||||||
|
// Type null|string is never int
|
||||||
|
'error_message' => 'RedundantCondition',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -972,28 +972,6 @@ class ValueTest extends \Psalm\Tests\TestCase
|
|||||||
}',
|
}',
|
||||||
'error_message' => 'RedundantCondition',
|
'error_message' => 'RedundantCondition',
|
||||||
],
|
],
|
||||||
'inArrayDetectType' => [
|
|
||||||
'<?php
|
|
||||||
function x($foo, string $bar): void {
|
|
||||||
if (!in_array($foo, [$bar], true)) {
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_string($foo)) {}
|
|
||||||
}',
|
|
||||||
// foo is always string
|
|
||||||
'error_message' => 'RedundantCondition',
|
|
||||||
],
|
|
||||||
'inArrayRemoveInvalid' => [
|
|
||||||
'<?php
|
|
||||||
function x(?string $foo, int $bar): void {
|
|
||||||
if (!in_array($foo, [$bar], true)) {
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
}',
|
|
||||||
// Type null|string is never int
|
|
||||||
'error_message' => 'RedundantCondition',
|
|
||||||
],
|
|
||||||
'neverNotIdenticalFloatType' => [
|
'neverNotIdenticalFloatType' => [
|
||||||
'<?php
|
'<?php
|
||||||
$a = 4.1;
|
$a = 4.1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user