1
0
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:
orklah 2021-09-20 16:49:15 +02:00 committed by GitHub
commit 994c927b6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 25 deletions

View File

@ -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) {

View File

@ -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();
} }
} }
} }

View File

@ -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',
],
]; ];
} }
} }

View File

@ -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;