1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Add negated identity with false case

This is the opposite of === true and works the same.
This commit is contained in:
Grégoire Paris 2020-07-27 20:26:27 +02:00 committed by Bruce Weirdan
parent c9f52c449b
commit 26f8e5b333
No known key found for this signature in database
GPG Key ID: CFC3AAB181751B0D
3 changed files with 77 additions and 33 deletions

View File

@ -12,4 +12,8 @@ function returnsABool(): bool {
if (returnsABool() === true) {
echo "hi!";
}
if (returnsABool() !== false) {
echo "hi!";
}
```

View File

@ -2076,41 +2076,58 @@ class AssertionFinder
if ($codebase
&& $source instanceof StatementsAnalyzer
&& ($var_type = $source->node_data->getType($base_conditional))
&& $conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical
) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
$false_type = Type::getFalse();
$config = $source->getCodebase()->config;
if (!UnionTypeComparator::isContainedBy(
$codebase,
$var_type,
$false_type
) && !UnionTypeComparator::isContainedBy(
$codebase,
$false_type,
$var_type
if ($config->strict_binary_operands
&& $var_type->isSingle()
&& $var_type->hasBool()
&& !$var_type->from_docblock
) {
if (IssueBuffer::accepts(
new RedundantIdentityWithTrue(
'The "!== false" part of this comparison is redundant',
new CodeLocation($source, $conditional)
),
$source->getSuppressedIssues()
)) {
if ($var_type->from_docblock) {
if (IssueBuffer::accepts(
new RedundantConditionGivenDocblockType(
'Docblock-defined type ' . $var_type . ' can never contain false',
new CodeLocation($source, $conditional),
$var_type->getId() . ' false'
),
$source->getSuppressedIssues()
)) {
// fall through
}
} else {
if (IssueBuffer::accepts(
new RedundantCondition(
$var_type . ' can never contain false',
new CodeLocation($source, $conditional),
$var_type->getId() . ' false'
),
$source->getSuppressedIssues()
)) {
// fall through
}
// fall through
}
}
$false_type = Type::getFalse();
if (!UnionTypeComparator::isContainedBy(
$codebase,
$var_type,
$false_type
) && !UnionTypeComparator::isContainedBy(
$codebase,
$false_type,
$var_type
)) {
if ($var_type->from_docblock) {
if (IssueBuffer::accepts(
new RedundantConditionGivenDocblockType(
'Docblock-defined type ' . $var_type . ' can never contain false',
new CodeLocation($source, $conditional),
$var_type->getId() . ' false'
),
$source->getSuppressedIssues()
)) {
// fall through
}
} else {
if (IssueBuffer::accepts(
new RedundantCondition(
$var_type . ' can never contain false',
new CodeLocation($source, $conditional),
$var_type->getId() . ' false'
),
$source->getSuppressedIssues()
)) {
// fall through
}
}
}

View File

@ -107,6 +107,29 @@ class BinaryOperationTest extends TestCase
$this->analyzeFile('somefile.php', new \Psalm\Context());
}
public function testStringFalseInequivalence(): void
{
$config = \Psalm\Config::getInstance();
$config->strict_binary_operands = true;
$this->addFile(
'somefile.php',
'<?php
function returnsABool(): bool {
return rand(1, 2) === 1;
}
if (returnsABool() !== false) {
echo "hi!";
}'
);
$this->expectException(\Psalm\Exception\CodeException::class);
$this->expectExceptionMessage('RedundantIdentityWithTrue');
$this->analyzeFile('somefile.php', new \Psalm\Context());
}
/**
* @return iterable<string,array{string,assertions?:array<string,string>,error_levels?:string[]}>
*/
@ -532,7 +555,7 @@ class BinaryOperationTest extends TestCase
],
'IntOverflowPlus' => [
'<?php
$a = 2**62 - 1 + 2**62;
$a = 2**62 - 1 + 2**62;
$b = 2**62 + 2**62 - 1; // plus results in a float',
'assertions' => [
'$a' => 'int',