mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
create a separate issue type
This commit is contained in:
parent
f3543ca9ab
commit
fb93aede12
@ -427,6 +427,7 @@
|
||||
<xs:element name="ReferenceReusedFromConfusingScope" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="ReservedWord" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="RiskyCast" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="RiskyTruthyFalsyComparison" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="StringIncrement" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="TaintedCallable" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="TaintedCookie" type="IssueHandlerType" minOccurs="0" />
|
||||
|
@ -173,6 +173,7 @@ Level 5 and above allows a more non-verifiable code, and higher levels are even
|
||||
- [TooManyArguments](issues/TooManyArguments.md)
|
||||
- [TypeDoesNotContainNull](issues/TypeDoesNotContainNull.md)
|
||||
- [TypeDoesNotContainType](issues/TypeDoesNotContainType.md)
|
||||
- [RiskyTruthyFalsyComparison](issues/RiskyTruthyFalsyComparison.md)
|
||||
- [UndefinedMagicMethod](issues/UndefinedMagicMethod.md)
|
||||
- [UndefinedMagicPropertyAssignment](issues/UndefinedMagicPropertyAssignment.md)
|
||||
- [UndefinedMagicPropertyFetch](issues/UndefinedMagicPropertyFetch.md)
|
||||
|
@ -229,6 +229,7 @@
|
||||
- [ReferenceReusedFromConfusingScope](issues/ReferenceReusedFromConfusingScope.md)
|
||||
- [ReservedWord](issues/ReservedWord.md)
|
||||
- [RiskyCast](issues/RiskyCast.md)
|
||||
- [RiskyTruthyFalsyComparison](issues/RiskyTruthyFalsyComparison.md)
|
||||
- [StringIncrement](issues/StringIncrement.md)
|
||||
- [TaintedCallable](issues/TaintedCallable.md)
|
||||
- [TaintedCookie](issues/TaintedCookie.md)
|
||||
|
29
docs/running_psalm/issues/RiskyTruthyFalsyComparison.md
Normal file
29
docs/running_psalm/issues/RiskyTruthyFalsyComparison.md
Normal file
@ -0,0 +1,29 @@
|
||||
# RiskyTruthyFalsyComparison
|
||||
|
||||
Emitted when comparing a value with multiple types that can both contain truthy and falsy values.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @param array|null $arg
|
||||
* @return void
|
||||
*/
|
||||
function foo($arg) {
|
||||
if ($arg) {
|
||||
// this is risky, bc the empty array and null case are handled together
|
||||
}
|
||||
|
||||
if (!$arg) {
|
||||
// this is risky, bc the empty array and null case are handled together
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Why this is bad
|
||||
|
||||
The truthy/falsy type of one variable is often forgotten and not handled explicitly causing hard to track down errors.
|
||||
|
||||
## How to fix
|
||||
|
||||
Explicitly validate the variable with strict comparison.
|
@ -15,6 +15,7 @@ use Psalm\Internal\Scope\IfScope;
|
||||
use Psalm\Issue\DocblockTypeContradiction;
|
||||
use Psalm\Issue\RedundantCondition;
|
||||
use Psalm\Issue\RedundantConditionGivenDocblockType;
|
||||
use Psalm\Issue\RiskyTruthyFalsyComparison;
|
||||
use Psalm\Issue\TypeDoesNotContainType;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Type\Atomic\TBool;
|
||||
@ -388,13 +389,13 @@ final class IfConditionalAnalyzer
|
||||
if ($has_both) {
|
||||
$both_types = $both_types->freeze();
|
||||
IssueBuffer::maybeAdd(
|
||||
new TypeDoesNotContainType(
|
||||
new RiskyTruthyFalsyComparison(
|
||||
'Operand of type ' . $type->getId() . ' contains ' .
|
||||
'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' .
|
||||
$both_types->getId() . ', which can be falsy and truthy. ' .
|
||||
'This can cause possibly unexpected behavior. Use strict comparison instead.',
|
||||
new CodeLocation($statements_analyzer, $stmt),
|
||||
$type->getId() . ' truthy-falsy',
|
||||
$type->getId(),
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues(),
|
||||
);
|
||||
|
@ -7,7 +7,7 @@ use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Issue\TypeDoesNotContainType;
|
||||
use Psalm\Issue\RiskyTruthyFalsyComparison;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TBool;
|
||||
@ -63,13 +63,13 @@ final class BooleanNotAnalyzer
|
||||
if ($has_both) {
|
||||
$both_types = $both_types->freeze();
|
||||
IssueBuffer::maybeAdd(
|
||||
new TypeDoesNotContainType(
|
||||
new RiskyTruthyFalsyComparison(
|
||||
'Operand of type ' . $expr_type->getId() . ' contains ' .
|
||||
'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' .
|
||||
$both_types->getId() . ', which can be falsy and truthy. ' .
|
||||
'This can cause possibly unexpected behavior. Use strict comparison instead.',
|
||||
new CodeLocation($statements_analyzer, $stmt),
|
||||
$expr_type->getId() . ' truthy-falsy',
|
||||
$expr_type->getId(),
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues(),
|
||||
);
|
||||
|
@ -8,7 +8,7 @@ use Psalm\Context;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Issue\ForbiddenCode;
|
||||
use Psalm\Issue\InvalidArgument;
|
||||
use Psalm\Issue\TypeDoesNotContainType;
|
||||
use Psalm\Issue\RiskyTruthyFalsyComparison;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TBool;
|
||||
@ -82,13 +82,13 @@ final class EmptyAnalyzer
|
||||
if ($has_both) {
|
||||
$both_types = $both_types->freeze();
|
||||
IssueBuffer::maybeAdd(
|
||||
new TypeDoesNotContainType(
|
||||
new RiskyTruthyFalsyComparison(
|
||||
'Operand of type ' . $expr_type->getId() . ' contains ' .
|
||||
'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' .
|
||||
$both_types->getId() . ', which can be falsy and truthy. ' .
|
||||
'This can cause possibly unexpected behavior. Use strict comparison instead.',
|
||||
new CodeLocation($statements_analyzer, $stmt),
|
||||
$expr_type->getId() . ' truthy-falsy',
|
||||
$expr_type->getId(),
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues(),
|
||||
);
|
||||
|
17
src/Psalm/Issue/RiskyTruthyFalsyComparison.php
Normal file
17
src/Psalm/Issue/RiskyTruthyFalsyComparison.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Issue;
|
||||
|
||||
use Psalm\CodeLocation;
|
||||
|
||||
final class RiskyTruthyFalsyComparison extends CodeIssue
|
||||
{
|
||||
public const ERROR_LEVEL = 4;
|
||||
public const SHORTCODE = 356;
|
||||
|
||||
public function __construct(string $message, CodeLocation $code_location, ?string $dupe_key)
|
||||
{
|
||||
parent::__construct($message, $code_location);
|
||||
$this->dupe_key = $dupe_key;
|
||||
}
|
||||
}
|
@ -3528,7 +3528,7 @@ class ConditionalTest extends TestCase
|
||||
if ($arg) {
|
||||
}
|
||||
}',
|
||||
'error_message' => 'TypeDoesNotContainType',
|
||||
'error_message' => 'RiskyTruthyFalsyComparison',
|
||||
],
|
||||
'nonStrictConditionTruthyFalsyNegated' => [
|
||||
'code' => '<?php
|
||||
@ -3540,7 +3540,7 @@ class ConditionalTest extends TestCase
|
||||
if (!$arg) {
|
||||
}
|
||||
}',
|
||||
'error_message' => 'TypeDoesNotContainType',
|
||||
'error_message' => 'RiskyTruthyFalsyComparison',
|
||||
],
|
||||
'nonStrictConditionTruthyFalsyFuncCall' => [
|
||||
'code' => '<?php
|
||||
@ -3558,7 +3558,7 @@ class ConditionalTest extends TestCase
|
||||
* @return array|null
|
||||
*/
|
||||
function bar($arg) {}',
|
||||
'error_message' => 'TypeDoesNotContainType',
|
||||
'error_message' => 'RiskyTruthyFalsyComparison',
|
||||
],
|
||||
'nonStrictConditionTruthyFalsyFuncCallNegated' => [
|
||||
'code' => '<?php
|
||||
@ -3576,7 +3576,7 @@ class ConditionalTest extends TestCase
|
||||
* @return array|null
|
||||
*/
|
||||
function bar($arg) {}',
|
||||
'error_message' => 'TypeDoesNotContainType',
|
||||
'error_message' => 'RiskyTruthyFalsyComparison',
|
||||
],
|
||||
'redundantConditionForNonEmptyString' => [
|
||||
'code' => '<?php
|
||||
|
Loading…
Reference in New Issue
Block a user