,error_levels?:string[]}> */ public function providerValidCodeParse() { return [ 'intIsMixed' => [ ' [ ' [], 'error_levels' => ['RedundantConditionGivenDocblockType'], ], 'arrayTypeResolutionFromDocblock' => [ ' [], 'error_levels' => ['RedundantConditionGivenDocblockType'], ], 'typeResolutionFromDocblockInside' => [ ' [], 'error_levels' => ['DocblockTypeContradiction'], ], 'notInstanceof' => [ ' [ '$out' => 'A|null', ], ], 'notInstanceOfProperty' => [ 'foo = new B(); } } $a = new A(); $out = null; if ($a->foo instanceof C) { // do something } else { $out = $a->foo; }', 'assertions' => [ '$out' => 'B|null', ], 'error_levels' => [], ], 'notInstanceOfPropertyElseif' => [ 'foo)) { } elseif ($a->foo instanceof C) { // do something } else { $out = $a->foo; }', 'assertions' => [ '$out' => 'B|null', ], 'error_levels' => [], ], 'typeRefinementWithIsNumericOnIntOrFalse' => [ ' [ ' [ ' 4 ? "hello" : 5; if (is_numeric($a)) { exit; }', 'assertions' => [ '$a' => 'string', ], ], 'typeRefinementWithStringOrTrue' => [ ' 4 ? "hello" : true; if (is_bool($a)) { exit; }', 'assertions' => [ '$a' => 'string', ], ], 'updateMultipleIssetVars' => [ ' [ ' [ ' [ ' [], 'error_levels' => ['RedundantConditionGivenDocblockType'], ], 'ignoreNullCheckAndMaintainNullValue' => [ ' [ '$b' => 'null', ], 'error_levels' => ['TypeDoesNotContainType', 'RedundantCondition'], ], 'ignoreNullCheckAndMaintainNullableValue' => [ ' [ '$b' => 'int|null', ], ], 'ternaryByRefVar' => [ ' [ ' [ ' [ 'bar(); $this->bat(); takesA($this); takesI($this); takesAandI($this); takesIandA($this); } } protected function bar(): void {} } class B extends A implements I { public function bat(): void {} }', ], 'createIntersectionOfInterfaceAndClass' => [ 'bat(); $i->baz(); } } function bar(A $a) : void { if ($a instanceof I) { $a->bat(); $a->baz(); } } class B extends A implements I { public function baz() : void {} } foo(new B); bar(new B);', ], 'unionOfArrayOrTraversable' => [ ' [ ' [ ' [ ' [ ' [ ' 2) { $a = "hello"; } else { $a = false; } } return $a; }', ], 'nullableIntReplacement' => [ ' [ '$a' => 'int|null', ], ], 'eraseNullAfterInequalityCheck' => [ ' 0) { echo $a + 3; } if (0 < $a) { echo $a + 3; }', ], 'twoWrongsDontMakeARight' => [ ' [ '$a' => 'false', ], ], 'instanceofStatic' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ 'foo(); }', ], 'SKIPPED-isArrayOnArrayKeyOffset' => [ '|string>} */ $doc = []; if (!is_array($doc["s"]["t"])) { $doc["s"]["t"] = [$doc["s"]["t"]]; }', 'assertions' => [ '$doc[\'s\'][\'t\']' => 'array', ], ], 'removeTrue' => [ ' [ ' [ ' [ '$a' => 'null', ], ], 'removeNullWithIsScalar' => [ ' [ '$a' => 'string', ], ], 'scalarToNumeric' => [ ' [ ' [ ' [ ' [ '|null $foo */ function d(?iterable $foo): void { if (is_iterable($foo)) { foreach ($foo as $f) {} } if (!is_iterable($foo)) { } else { foreach ($foo as $f) {} } }', ], 'isStringServerVar' => [ ' [ ' [ 'b())) {} }', ], 'reconcileFloatToEmpty' => [ ' [ ' [ ' $v */ function foo(array $v) : void { if (!isset($v[0])) { return; } if ($v[0] === " ") { array_shift($v); } if (!isset($v[0])) {} }', ], 'arrayEquality' => [ '> $haystack * @param array $needle */ function foo(array $haystack, array $needle) : void { foreach ($haystack as $arr) { if ($arr === $needle) {} } }', ], 'classResolvesBackToSelfAfterComparison' => [ ' [ '$a' => 'A', ], ], 'isNumericCanBeScalar' => [ ' [ '|null $val */ function foo(?string $val) : void { if (!$val) {} if ($val) {} }', ], 'allowStringToObjectReconciliation' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' $a * @return array{from:bool, to: bool} */ function foo(array $a) : array { return $a; }', ], 'dontChangeScalar' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ 'foo(); } function baz(A $a) : void { if ((!$a instanceof B || !$a instanceof C) === false) { return; } $a->foo(); }', ], 'selfInstanceofStatic' => [ ' [ ' [ 'foo) { $this->foo = []; } } public function iffer() : bool { return $this->foo || $this->bar; } }', ], 'noLeakyForeachType' => [ '_array_value = $this->getArrayValue(); if ($this->_array_value !== null && !count($this->_array_value)) { return; } switch ($var) { case "a": foreach ($this->_array_value ?: [] as $v) {} break; case "b": foreach ($this->_array_value ?: [] as $v) {} break; } } }', [], ['MixedAssignment'], ], 'nonEmptyThing' => [ ' [ ' $b */ function foo(array $a, array $b) : void { if ($a === $b) {} }', ], 'preventCombinatorialExpansion' => [ ' [ ' $x */ function takesArray (array $x): void {} /** @var iterable */ $x = null; assert(is_array($x)); takesArray($x); /** * @param Traversable $x */ function takesTraversable (Traversable $x): void {} /** @var iterable */ $x = null; assert($x instanceof Traversable); takesTraversable($x);', ], 'dontReconcileArrayOffset' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ 'id])) {} }', ], 'assertArrayReturnTypeNarrowed' => [ ' [ ' [ ' [ 'format("Y-m-d"); } }', ], 'assertCheckOnNonZeroArrayOffset' => [ ' [ ' $arr */ function uriToPath(array $arr) : string { if (!isset($arr["a"]) || $arr["b"] !== "foo") { throw new \InvalidArgumentException("bad"); } return (string) $arr["c"]; }', ], 'combineAfterLoopAssert' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' 0; }', ], 'assertHasArrayAccess' => [ ' [ '> $array * @return array */ function getBar(array $array) : array { if (isset($array[\'foo\'][\'bar\'])) { return $array[\'foo\']; } return []; }', ], 'assertHasArrayAccessOnSimpleXMLElement' => [ 'bar)) {} }', ], 'assertArrayOffsetToTraversable' => [ ' [ ' [ ' [ ' $arr * @return non-empty-array */ function foo(array $arr) : array { if (isset($arr["a"])) { return $arr; } return ["b" => 1]; }' ], 'setArrayConstantOffset' => [ ' [ ' $arr */ function foo(A $a, array $arr): void { if (!isset($arr[$a->id])) { $arr[$a->id] = new B(); } $arr[$a->id]->foo(); }' ], 'assertAfterNotEmptyArrayCheck' => [ ' [ 'c[$s]) && empty($this->c[$t])) {} } }' ], 'assertNotEmptyTwiceOnStaticPropertyArray' => [ ' [ ' [ ' [ ' [ 'arr[0])) { return $this->arr[0]; } $this->arr[0] = new stdClass; return $this->arr[0]; } }' ], 'assertArrayKeyExistsRefinesType' => [ ' */ public const DAYS = [ 1 => "mon", 2 => "tue", 3 => "wed", 4 => "thu", 5 => "fri", 6 => "sat", 7 => "sun", ]; /** @param key-of $dayNum*/ private static function doGetDayName(int $dayNum): string { return self::DAYS[$dayNum]; } /** @throws LogicException */ public static function getDayName(int $dayNum): string { if (! array_key_exists($dayNum, self::DAYS)) { throw new \LogicException(); } return self::doGetDayName($dayNum); } }' ], 'assertPropertiesOfElseStatement' => [ 'a === "foo") { } elseif ($obj->b === "bar") { } else if ($obj->b === "baz") {} if ($obj->b === "baz") {} }' ], 'assertPropertiesOfElseifStatement' => [ 'a === "foo") { } elseif ($obj->b === "bar") { } elseif ($obj->b === "baz") {} if ($obj->b === "baz") {} }' ], 'assertArrayWithOffset' => [ ' [ ' [ ' [ 'foo()) { return $value; } return new O(); }' ], 'SKIPPED-assertVarRedefinedInIfWithOr' => [ ' [ ' [ 'foo();', [ '$a' => 'bool', ] ], 'SKIPPED-assertVarRedefinedInOpWithOr' => [ 'foo();', [ '$a' => 'bool', ] ], 'assertVarInOrAfterAnd' => [ ' [ 'b && !$b->b; echo $a->b ? 1 : 0; }' ], 'assertAssertionsWithCreation' => [ ' [ 'foo(); } }' ], 'definedInConditionalAndCheckedInSubbranch' => [ 'foo(); } } }' ], 'definedInRhsOfConditionalInNegation' => [ 'foo(); } }' ], 'literalStringComparisonInIf' => [ ' [ ' [ ' [ 'foo(); }' ], 'assertOnArrayThings' => [ '> */ $a = null; if (isset($a["b"]) || isset($a["c"])) { $all_params = ($a["b"] ?? []) + ($a["c"] ?? []); }' ], 'assertOnNestedLogic' => [ ' 5) {} } }' ], 'arrayUnionTypeSwitching' => [ ' $map */ function foo(array $map, string $o) : void { if ($mapped_type = $map[$o] ?? null) { if (is_int($mapped_type)) { return; } } if (($mapped_type = $map[""] ?? null) && is_string($mapped_type)) { } }' ], 'propertySetOnElementInConditional' => [ 'old) && is_string($diff_elem->new)) || (is_int($diff_elem->old) && is_int($diff_elem->new)) ) { } }' ], 'manyNestedAsserts' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' */ public function providerInvalidCodeParse() { return [ 'makeNonNullableNull' => [ ' 'TypeDoesNotContainNull', ], 'makeInstanceOfThingInElseif' => [ ' 5 ? new A(): new B(); if ($a instanceof A) { } elseif ($a instanceof C) { }', 'error_message' => 'TypeDoesNotContainType', ], 'functionValueIsNotType' => [ ' 'TypeDoesNotContainType', ], 'stringIsNotTnt' => [ ' 'TypeDoesNotContainType', ], 'stringIsNotNull' => [ ' 'TypeDoesNotContainNull', ], 'stringIsNotFalse' => [ ' 'TypeDoesNotContainType', ], 'typeTransformation' => [ ' 'TypeDoesNotContainType', ], 'dontEraseNullAfterLessThanCheck' => [ ' 'PossiblyNullOperand', ], 'dontEraseNullAfterGreaterThanCheck' => [ ' $a) { echo $a + 3; }', 'error_message' => 'PossiblyNullOperand', ], 'nonRedundantConditionGivenDocblockType' => [ ' 'TypeDoesNotContainType', ], 'lessSpecificArrayFields' => [ ' "name"]);', 'error_message' => 'InvalidArgument', ], 'intersectionIncorrect' => [ ' 'InvalidArgument', ], 'catchTypeMismatchInBinaryOp' => [ ' */ function getStrings(): array { return ["hello", "world", 50]; } $a = getStrings(); if (is_bool($a[0]) && $a[0]) {}', 'error_message' => 'DocblockTypeContradiction', ], 'preventWeakEqualityToObject' => [ ' 'TypeDoesNotContainType', ], 'properReconciliationInElseIf' => [ ' 'RedundantCondition', ], 'allRemovalOfStringWithIsScalar' => [ ' 'RedundantCondition', ], 'noRemovalOfStringWithIsScalar' => [ ' 'TypeDoesNotContainType', ], 'impossibleNullEquality' => [ ' 'TypeDoesNotContainNull', ], 'impossibleTrueEquality' => [ ' 'TypeDoesNotContainType', ], 'impossibleFalseEquality' => [ ' 'TypeDoesNotContainType', ], 'impossibleNumberEquality' => [ ' 'TypeDoesNotContainType', ], 'SKIPPED-noIntersectionOfArrayOrTraversable' => [ ' 'TypeDoesNotContainType', ], 'scalarToBoolContradiction' => [ ' 'ParadoxicalCondition', ], 'noCrashWhenCastingArray' => [ ' 1, "b" => 2]; }', 'error_message' => 'InvalidReturnStatement', ], 'preventStrongEqualityScalarType' => [ ' 'TypeDoesNotContainType', ], 'preventYodaStrongEqualityScalarType' => [ ' 'TypeDoesNotContainType', ], 'classCannotNotBeSelf' => [ ' 'RedundantCondition', ], 'preventImpossibleComparisonToTrue' => [ ' 'DocblockTypeContradiction', ], 'preventAlwaysPossibleComparisonToTrue' => [ ' 'RedundantConditionGivenDocblockType', ], 'preventAlwaysImpossibleComparisonToFalse' => [ ' 'TypeDoesNotContainType', ], 'preventAlwaysPossibleComparisonToFalse' => [ ' 'RedundantCondition', ], 'nullCoalesceImpossible' => [ ' 'TypeDoesNotContainType' ], 'allowEmptyScalarAndNonEmptyScalarAssertions1' => [ ' 'RedundantCondition', ], 'allowEmptyScalarAndNonEmptyScalarAssertions2' => [ ' 'RedundantCondition', ], 'allowEmptyScalarAndNonEmptyScalarAssertions3' => [ ' 'RedundantCondition', ], 'allowEmptyScalarAndNonEmptyScalarAssertions4' => [ ' 'RedundantCondition', ], ]; } }