,error_levels?:string[]}> */ public function providerValidCodeParse(): iterable { return [ 'ignoreIssueAndAssign' => [ ' [ '$b' => 'null|stdClass', ], 'error_levels' => ['RedundantCondition'], ], 'byrefNoRedundantCondition' => [ ' [], 'error_levels' => [ 'RedundantConditionGivenDocblockType', 'DocblockTypeContradiction', ], ], 'assignmentInIf' => [ ' [ ' [], 'error_levels' => [ 'RedundantConditionGivenDocblockType', ], ], 'noRedundantConditionAfterDocblockTypeNullCheck' => [ 'foo) {} break; default: break; } }', 'assertions' => [], 'error_levels' => ['DocblockTypeContradiction'], ], 'noRedundantConditionTypeReplacementWithDocblock' => [ ' [], 'error_levels' => [ 'DocblockTypeContradiction', ], ], 'noRedundantConditionAfterPossiblyNullCheck' => [ ' [], 'error_levels' => ['PossiblyUndefinedGlobalVariable'], ], 'noRedundantConditionAfterFromDocblockRemoval' => [ 'foo() || $a->bar()) {}', 'assertions' => [], 'error_levels' => [ 'DocblockTypeContradiction', ], ], 'noEmptyUndefinedArrayVar' => [ ' [], 'error_levels' => ['MixedAssignment', 'MixedArrayAccess'], ], 'noComplaintWithIsNumericThenIsEmpty' => [ ' [ ' 0) { throw new RuntimeException("Failed"); } $value = new stdClass(); if (rand() % 2 > 0) { throw new RuntimeException("Failed"); } } catch (Exception $e) { if ($value) { var_export($value); } } if ($value) {} }', ], 'noRedundantConditionInFalseCheck' => [ ' [ 'next) {} } }', ], 'noRedundantConditionComparingBool' => [ ' [ ' [], 'error_levels' => [ 'DocblockTypeContradiction', 'RedundantConditionGivenDocblockType', ], ], 'evaluateArrayCheck' => [ ' false]; while (rand(0, 1) > 0 && !$data["f"]) { $data = ["f" => true]; } }', ], 'mixedArrayAssignment' => [ ' [], 'error_levels' => ['MixedAssignment', 'MixedArrayAccess'], ], 'hardPhpTypeAssertionsOnDocblockBoolType' => [ ' [], 'error_levels' => ['DocblockTypeContradiction'], ], 'hardPhpTypeAssertionsOnDocblockStringType' => [ ' [], 'error_levels' => ['DocblockTypeContradiction'], ], 'isObjectAssertionOnDocblockType' => [ ' [], 'error_levels' => ['RedundantConditionGivenDocblockType', 'DocblockTypeContradiction'], ], 'nullToMixedWithNullCheckWithArraykey' => [ ' */ function getStrings(): array { return ["hello", "world", 50]; } $a = getStrings(); if (is_string($a[0]) && strlen($a[0]) > 3) {}', 'assignments' => [], 'error_levels' => [], ], 'nullToMixedWithNullCheckWithIntKey' => [ ' */ function getStrings(): array { return ["hello", "world", 50]; } $a = getStrings(); if (is_string($a[0]) && strlen($a[0]) > 3) {}', 'assignments' => [], 'error_levels' => [], ], 'replaceFalseTypeWithTrueConditionalOnMixedEquality' => [ ' [], 'error_levels' => ['MixedAssignment', 'MissingReturnType', 'MixedArrayAccess'], ], 'nullCoalescePossiblyUndefined' => [ ' true]; } $option = $options["option"] ?? false; if ($option) {}', 'assignments' => [], 'error_levels' => ['MixedAssignment', 'MixedArrayAccess'], ], 'allowIntValueCheckAfterComparisonDueToOverflow' => [ ' [ ' [ ' [ ' [ ' "value"]; if (rand(0, 1)) { $x = []; } if ($x) { var_export($x); }', ], 'arrayKeyExistsAccess' => [ ' $arr */ function foo(array $arr) : void { if (array_key_exists(1, $arr)) { $a = ($arr[1] === "b") ? true : false; } }', ], 'noRedundantConditionStringNotFalse' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' ['MissingParamType'], ], 'notAlwaysTrueBinaryOp' => [ ' ['MissingParamType'], ], 'noRedundantConditionAfterAssertingValue' => [ ' [ ' [ ' $_) { if (rand(0, 1)) { unset($a["foo"][$key]); } } if (empty($a["foo"])) {} } }', [], ['MixedAssignment', 'MixedArrayAccess', 'MixedArrayOffset'], ], 'emptyKnowingArrayType' => [ '> $a */ function foo(array $a) : void { if (!empty($a["foo"])) { foreach ($a["foo"] as $key => $_) { if (rand(0, 1)) { unset($a["foo"][$key]); } } if (empty($a["foo"])) {} } }', ], 'suppressRedundantConditionAfterAssertNonEmpty' => [ ' $a */ function process(array $a): void { assert(!empty($a)); /** @psalm-suppress RedundantConditionGivenDocblockType */ assert(is_int($a[0])); }', ], 'allowChecksOnFalsyIf' => [ ' [ ' 1]; unset($dict[$s]); if (count($dict)) {} }', ], 'updateArrayAfterUnsetInLoop' => [ ' 1, "b" => 2, "c" => 3]; foreach ($arr as $v) { unset($dict[$v]); } if (count($dict)) {} }', ], 'noRedundantConditionWhenAssertingOnIntersection' => [ ' [ ' [ ' [ ' [ ' $tokens */ function propertyInUse(array $tokens, int $i): bool { if ($tokens[$i] !== 1) { return false; } $i++; if ($tokens[$i] !== 2) {} return false; }' ], 'invalidateAfterAssignOp' => [ ' $tokens */ function propertyInUse(array $tokens, int $i): bool { if ($tokens[$i] !== 1) { return false; } $i += 1; if ($tokens[$i] !== 2) {} return false; }' ], 'invalidateAfterAssign' => [ ' $tokens */ function propertyInUse(array $tokens, int $i): bool { if ($tokens[$i] !== 1) { return false; } $i = $i + 1; if ($tokens[$i] !== 2) {} return false; }' ], 'numericNotString' => [ ' [ ' 'closed-resource', ] ], 'allowCheckOnReturnTypeUnion' => [ ' [ 'closed) { return function() : void { if ($this->closed) {} }; } return function() : void {}; } }' ], ]; } /** * @return iterable */ public function providerInvalidCodeParse(): iterable { return [ 'ifFalse' => [ ' 'TypeDoesNotContainType', ], 'ifNotTrue' => [ ' 'TypeDoesNotContainType', ], 'ifTrue' => [ ' 'RedundantCondition', ], 'unnecessaryInstanceof' => [ 'fooFoo(); }', 'error_message' => 'RedundantCondition', ], 'failedTypeResolution' => [ ' 'RedundantCondition', ], 'failedTypeResolutionWithDocblock' => [ ' 'RedundantCondition', ], 'typeResolutionFromDocblockAndInstanceof' => [ ' 'RedundantCondition', ], 'typeResolutionRepeatingConditionWithSingleVar' => [ ' 5; if ($a && $a) {}', 'error_message' => 'RedundantCondition', ], 'typeResolutionRepeatingConditionWithVarInMiddle' => [ ' 5; $b = rand(0, 10) > 5; if ($a && $b && $a) {}', 'error_message' => 'RedundantCondition', ], 'typeResolutionRepeatingOredConditionWithSingleVar' => [ ' 5; if ($a || $a) {}', 'error_message' => 'ParadoxicalCondition', ], 'typeResolutionRepeatingOredConditionWithVarInMiddle' => [ ' 5; $b = rand(0, 10) > 5; if ($a || $b || $a) {}', 'error_message' => 'ParadoxicalCondition', ], 'typeResolutionIsIntAndIsNumeric' => [ ' 5 ? "hello" : 3; if (is_int($c) && is_numeric($c)) {}', 'error_message' => 'RedundantCondition', ], 'typeResolutionWithInstanceOfAndNotEmpty' => [ ' 5 ? new stdClass : null; if ($x instanceof stdClass && $x) {}', 'error_message' => 'RedundantCondition', ], 'methodWithMeaninglessCheck' => [ 'fooFoo(); } }', 'error_message' => 'TypeDoesNotContainType', ], 'twoVarLogicNotNestedWithElseifNegatedInIf' => [ ' 'RedundantCondition', ], 'refineTypeInMethodCall' => [ ' 'RedundantCondition - src' . DIRECTORY_SEPARATOR . 'somefile.php:15', ], 'replaceFalseType' => [ ' 'RedundantCondition', ], 'replaceTrueType' => [ ' 'TypeDoesNotContainType - src' . DIRECTORY_SEPARATOR . 'somefile.php:7', ], 'disallowFloatCheckAfterSettingToVar' => [ ' 'TypeDoesNotContainType - src' . DIRECTORY_SEPARATOR . 'somefile.php:7', ], 'disallowTwoIntValueChecksDueToConditionalOverflow' => [ ' 'TypeDoesNotContainType - src' . DIRECTORY_SEPARATOR . 'somefile.php:6', ], 'redundantEmptyArray' => [ ' "value"]; if ($x) { var_export($x); }', 'error_message' => 'RedundantCondition', ], 'redundantConditionStringNotFalse' => [ ' 'RedundantCondition', ], 'redundantConditionStringNotTrue' => [ ' 'RedundantCondition', ], 'redundantConditionAfterRemovingFalse' => [ ' 'RedundantCondition', ], 'redundantConditionAfterRemovingTrue' => [ ' 'RedundantCondition', ], 'impossibleNullEquality' => [ ' 'RedundantCondition', ], 'impossibleTrueEquality' => [ ' 'RedundantCondition', ], 'impossibleFalseEquality' => [ ' 'RedundantCondition', ], 'impossibleNumberEquality' => [ ' 'RedundantCondition', ], 'alwaysTrueBinaryOp' => [ ' 'RedundantCondition', 'error_levels' => ['MissingParamType'], ], 'negatedInstanceof' => [ ' 'RedundantCondition', ], 'redundantInstanceof' => [ ' 'RedundantConditionGivenDocblockType', ], 'preventDocblockTypesBeingIdenticalToTrue' => [ ' 'DocblockTypeContradiction', ], 'preventDocblockTypesBeingIdenticalToTrueReversed' => [ ' 'DocblockTypeContradiction', ], 'preventDocblockTypesBeingIdenticalToFalse' => [ ' 'DocblockTypeContradiction', ], 'preventDocblockTypesBeingIdenticalToFalseReversed' => [ ' 'DocblockTypeContradiction', ], 'preventDocblockTypesBeingSameAsEmptyArrayReversed' => [ ' 'DocblockTypeContradiction', ], 'preventDocblockTypesBeingIdenticalToEmptyArrayReversed' => [ ' 'DocblockTypeContradiction', ], 'preventTypesBeingIdenticalToEmptyArrayReversed' => [ ' 'TypeDoesNotContainType', ], 'SKIPPED-secondInterfaceAssertionIsRedundant' => [ ' 'RedundantConditionGivenDocblockType', ], 'errorAfterStatementThatCannotBeConvertedToAssertion' => [ ' 'RedundantCondition', ], 'SKIPPED-noLongerWarnsAboutRedundancyHere' => [ ' 'RedundantCondition', ], 'prohibitFalsyChecksOnPropertiesWithMethodCall' => [ 'headers = $headers; } } function lag(Request $req) : void { if ($req->headers && $req->headers->has("foo")) {} }', 'error_message' => 'RedundantCondition', ], 'prohibitFalsyChecksOnPropertiesWithoutMethodCall' => [ 'headers = $headers; } } function lag(Request $req) : void { if ($req->headers) {} }', 'error_message' => 'RedundantCondition', ], 'checkResourceTwice' => [ ' 'RedundantCondition', ], 'preventAlwaysReturningInt' => [ ' 'RedundantCondition', ], 'preventAlwaysReturningSpecificInt' => [ ' 'RedundantConditionGivenDocblockType', ], 'preventNotAlwaysReturningInt' => [ ' 'TypeDoesNotContainType', ], 'classAlwaysParent' => [ ' 'RedundantCondition', ], 'staticClassIsAlwaysNull' => [ ' 'RedundantConditionGivenDocblockType', ], 'classStringNotEmpty' => [ ' 'RedundantCondition', ], 'leftCannotBeTrue' => [ ' 'DocblockTypeContradiction', ], 'rightCannotBeTrue' => [ ' 'DocblockTypeContradiction', ], ]; } }