,error_levels?:string[]}> */ public function providerValidCodeParse() { return [ 'arrayFilter' => [ ' 5, "b" => 12, "c" => null]); $e = array_filter( ["a" => 5, "b" => 12, "c" => null], function(?int $i): bool { return true; } );', 'assertions' => [ '$d' => 'array', '$e' => 'array', ], ], 'arrayFilterAdvanced' => [ ' 5, "b" => 12, "c" => null], function(?int $val, string $key): bool { return true; }, ARRAY_FILTER_USE_BOTH); $g = array_filter(["a" => 5, "b" => 12, "c" => null], function(string $val): bool { return true; }, ARRAY_FILTER_USE_KEY); $bar = "bar"; $foo = [ $bar => function (): string { return "baz"; }, ]; $foo = array_filter( $foo, function (string $key): bool { return $key === "bar"; }, ARRAY_FILTER_USE_KEY );', 'assertions' => [ '$f' => 'array', '$g' => 'array', ], ], 'arrayFilterIgnoreNullable' => [ ' */ public function getRows() : array { return [new self, null]; } public function filter() : void { $arr = array_filter( static::getRows(), function (self $row) : bool { return is_a($row, static::class); } ); } }', 'assertions' => [], 'error_levels' => ['PossiblyInvalidArgument'], ], 'arrayFilterAllowTrim' => [ ' [ ' [ ' $a */ function fooFoo(array $a = []): void { }', ], 'abs' => [ ' [ '$a' => 'int', '$b' => 'float', '$c' => 'numeric|null', ], 'error_levels' => ['MixedAssignment', 'MixedArgument'], ], 'validDocblockParamDefault' => [ ' [ ' [ ' [ 'foo);', ], 'namespaced' => [ ' [ ' [ ' [ ' 1, "b" => 2]);', 'assertions' => [ '$a' => 'array', ], ], 'arrayKeysMixed' => [ ' 5]; $a = array_keys($b);', 'assertions' => [ '$a' => 'array', ], 'error_levels' => ['MixedArgument'], ], 'arrayValues' => [ ' 1, "b" => 2]);', 'assertions' => [ '$b' => 'array', ], ], 'arrayCombine' => [ ' [ '$c' => 'array|false', ], ], 'arrayCombineFalse' => [ ' [ '$c' => 'array|false', ], ], 'arrayMerge' => [ ' [ '$d' => 'array{0:string, 1:string, 2:string, 3:int, 4:int, 5:int}', ], ], 'arrayReverseDontPreserveKey' => [ ' 4]);', 'assertions' => [ '$d' => 'non-empty-array', ], ], 'arrayReverseDontPreserveKeyExplicitArg' => [ ' 4], false);', 'assertions' => [ '$d' => 'non-empty-array', ], ], 'arrayReversePreserveKey' => [ ' [ '$d' => 'non-empty-array', ], ], 'arrayDiff' => [ ' 5, "b" => 12], [5]);', 'assertions' => [ '$d' => 'array', ], ], 'arrayPopMixed' => [ ' 5, "c" => 6]; $a = array_pop($b);', 'assertions' => [ '$a' => 'mixed', '$b' => 'mixed', ], 'error_levels' => ['MixedAssignment', 'MixedArgument'], ], 'arrayPopNonEmpty' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if ($a) { $b = array_pop($a); } $c = array_pop($a);', 'assertions' => [ '$b' => 'int', '$c' => 'int|null', ], ], 'arrayPopNonEmptyAfterIsset' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (isset($a["a"])) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterCount' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (count($a)) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'noRedundantConditionAfterArrayObjectCountCheck' => [ ' */ $a = []; $b = 5; if (count($a)) {}', ], 'noRedundantConditionAfterMixedOrEmptyArrayCountCheck' => [ ' [], 'error_levels' => ['MixedAssignment', 'MixedArgument'], ], 'objectLikeArrayAssignmentInConditional' => [ ' [ ' [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (count($a) === 1) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterCountSoftEqualsOne' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (count($a) == 1) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterCountGreaterThanOne' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (count($a) > 0) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterCountGreaterOrEqualsOne' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (count($a) >= 1) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterCountEqualsOneReversed' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (1 === count($a)) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterCountSoftEqualsOneReversed' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (1 == count($a)) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterCountGreaterThanOneReversed' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (0 < count($a)) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterCountGreatorOrEqualToOneReversed' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $b = 5; if (1 <= count($a)) { $b = array_pop($a); }', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterThreeAssertions' => [ ' */ public $arr = []; } /** @var array */ $replacement_stmts = []; if (!$replacement_stmts || !$replacement_stmts[0] instanceof B || count($replacement_stmts[0]->arr) > 1 ) { return null; } $b = $replacement_stmts[0]->arr;', 'assertions' => [ '$b' => 'array', ], ], 'arrayPopNonEmptyAfterArrayAddition' => [ ' */ $a = ["a" => 5, "b" => 6, "c" => 7]; $a["foo"] = 10; $b = array_pop($a);', 'assertions' => [ '$b' => 'int', ], ], 'arrayPopNonEmptyAfterMixedArrayAddition' => [ ' 5, "b" => 6, "c" => 7]; $a[] = "hello"; $b = array_pop($a);', 'assertions' => [ '$b' => 'string|mixed', ], 'error_levels' => [ 'MixedAssignment', ], ], 'countMoreThan0CanBeInverted' => [ ' 0) { exit; }', 'assertions' => [ '$a' => 'array', ], ], 'uasort' => [ ' [], 'error_levels' => [ 'MixedArrayAccess', 'MixedArgument', 'MissingClosureParamType', 'MissingClosureReturnType', ], ], 'byRefAfterCallable' => [ ' [], 'error_levels' => [ 'MixedAssignment', 'MixedArrayAccess', 'RedundantConditionGivenDocblockType', ], ], 'ignoreNullablePregReplace' => [ ' [ ' "bar"]; extract($a); takesString($foo);', 'assertions' => [], 'error_levels' => [ 'MixedAssignment', 'MixedArrayAccess', 'MixedArgument', ], ], 'arrayMergeObjectLike' => [ ' $a * @return array */ function foo($a) { return $a; } $a1 = ["hi" => 3]; $a2 = ["bye" => 5]; $a3 = array_merge($a1, $a2); foo($a3);', 'assertions' => [ '$a3' => 'array{hi:int, bye:int}', ], ], 'arrayRand' => [ ' "a", "y" => "b"]; $c = array_rand($vars); $d = $vars[$c]; $more_vars = ["a", "b"]; $e = array_rand($more_vars);', 'assertions' => [ '$vars' => 'array{x:string, y:string}', '$c' => 'string', '$d' => 'string', '$more_vars' => 'array{0:string, 1:string}', '$e' => 'int', ], ], 'arrayRandMultiple' => [ ' "a", "y" => "b"]; $b = 3; $c = array_rand($vars, 1); $d = array_rand($vars, 2); $e = array_rand($vars, 3); $f = array_rand($vars, $b);', 'assertions' => [ '$vars' => 'array{x:string, y:string}', '$c' => 'string', '$e' => 'array', '$f' => 'array|string', ], ], 'arrayKeysNoEmpty' => [ ' [], 'error_levels' => ['MixedAssignment', 'MixedArgument'], ], 'compact' => [ ' [ ' $b */ function a($b): string { return $b["a"]; } a(["a" => "hello"]);', ], 'objectLikeKeyChecksAgainstObjectLike' => [ ' "hello"]);', ], 'getenv' => [ ' [ '$a' => 'array', '$b' => 'string|false', ], ], 'arrayPopNotNullable' => [ ' $list */ function test(array $list) : void { while (!empty($list)) { $tmp = array_pop($list); expectsInt($tmp["item"]); } }', ], 'arrayFilterWithAssert' => [ ' [ '$a' => 'array', ], 'error_levels' => [ 'MissingClosureParamType', ], ], 'arrayFilterUseKey' => [ ' function (): string { return "baz"; }, ]; $foo = array_filter( $foo, function (string $key): bool { return $key === "bar"; }, ARRAY_FILTER_USE_KEY );', 'assertions' => [ '$foo' => 'array', ], ], 'ignoreFalsableCurrent' => [ ' [ ' [ ' [ '$foo' => 'float|int', ], ], 'arrayMapObjectLikeAndCallable' => [ ' 1, "key2"=> "2"]; $r = array_map("intval", $v); return $r; }', ], 'arrayMapObjectLikeAndClosure' => [ ' 1, "key2"=> "2"]; $r = array_map(function($i) : int { return intval($i);}, $v); return $r; }', 'assertions' => [], 'error_levels' => [ 'MissingClosureParamType', 'MixedTypeCoercion', ], ], 'arrayFilterGoodArgs' => [ ' [ ' [], 'error_levels' => ['UndefinedClass'], ], 'arrayFilterIgnoreMissingMethod' => [ ' [], 'error_levels' => ['UndefinedMethod'], ], 'validCallables' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ '$a' => 'string', ], ], 'varExportConstFetch' => [ ' [ ' 1, "two" => 3]; $b = key($a); $c = $a[$b];', 'assertions' => [ '$b' => 'null|string', '$c' => 'int', ], ], 'explodeWithPossiblyFalse' => [ ' */ function exploder(string $s) : array { return explode(" ", $s); }', ], 'allowPossiblyUndefinedClassInClassExists' => [ ' [ ' [], 'error_levels' => ['MixedMethodCall'], ], 'next' => [ ' [ '$n' => 'string|false', ], ], 'iteratorToArray' => [ ' */ function generator(): Generator { yield new stdClass; } $a = iterator_to_array(generator());', 'assertions' => [ '$a' => 'array', ], ], 'iteratorToArrayWithGetIterator' => [ ' */ public function getIterator() { yield 1 => "1"; } } $a = iterator_to_array(new C);', 'assertions' => [ '$a' => 'array', ], ], 'arrayColumnInference' => [ '> */ function makeGenericArray(): array { return []; } /** @return array */ function makeShapeArray(): array { return []; } /** @return array */ function makeUnionArray(): array { return []; } $a = array_column([[1], [2], [3]], 0); $b = array_column([["a" => 1], ["a" => 2], ["a" => 3]], "a"); $c = array_column([["k" => "a", "v" => 1], ["k" => "b", "v" => 2]], "v", "k"); $d = array_column([], 0); $e = array_column(makeMixedArray(), 0); $f = array_column(makeGenericArray(), 0); $g = array_column(makeShapeArray(), 0); $h = array_column(makeUnionArray(), 0); ', 'assertions' => [ '$a' => 'array', '$b' => 'array', '$c' => 'array', '$d' => 'array', '$e' => 'array', '$f' => 'array', '$g' => 'array', '$h' => 'array', ], ], 'strtrWithPossiblyFalseFirstArg' => [ ' $replace_pairs * @return string */ function strtr_wrapper($str, array $replace_pairs) { /** @psalm-suppress PossiblyFalseArgument */ return strtr($str, $replace_pairs); }', ], 'splatArrayIntersect' => [ ' [ '$bar' => 'array', ], ], 'arrayReduce' => [ ' [ '$direct_closure_result' => 'int', '$passed_closure_result' => 'int', '$function_call_result' => 'int', ], ], 'arrayReduceMixedReturn' => [ ' [], 'error_levels' => ['MissingClosureReturnType', 'MixedAssignment'], ], 'versionCompare' => [ ' [ '$a' => 'int', '$b' => 'bool', '$c' => 'bool|null', ], ], 'getTimeOfDay' => [ ' [ '$a' => 'float', '$b' => 'array', '$c' => 'array', ], ], 'parseUrlArray' => [ ' [ '$porta' => 'int|null', '$porte' => 'int|null', ], 'error_levels' => ['MixedReturnStatement', 'MixedInferredReturnType'], ], 'parseUrlComponent' => [ ' [ ' [ ' [], 'error_levels' => ['MixedMethodCall'], ], 'arraySplice' => [ ' [ '$a' => 'non-empty-array', '$b' => 'array{0:string, 1:string, 2:string}', '$c' => 'array{0:int, 1:int, 2:int}', ], ], 'arraySpliceOtherType' => [ ' [ '$d' => 'array', ], ], 'ksortPreserveShape' => [ ' 3, "b" => 4]; ksort($a); acceptsAShape($a); /** * @param array{a:int,b:int} $a */ function acceptsAShape(array $a): void {}', ], 'suppressError' => [ ' [ '$a' => 'string|false', ], ], 'arraySlicePreserveKeys' => [ ' 1, "b" => 2, "c" => 3]; $b = array_slice($a, 1, 2, true); $c = array_slice($a, 1, 2, false); $d = array_slice($a, 1, 2);', 'assertions' => [ '$b' => 'non-empty-array', '$c' => 'array', '$d' => 'array', ], ], 'printrOutput' => [ ' [ ' [ '$a' => 'float', '$b' => 'string', '$c' => 'float|string', '$d' => 'string', ], ], 'filterVar' => [ ' ["default" => null]]); } function filterIntWithDefault(string $s) : int { return filter_var($s, FILTER_VALIDATE_INT, ["options" => ["default" => 5]]); } function filterBool(string $s) : bool { return filter_var($s, FILTER_VALIDATE_BOOLEAN); } function filterNullableBool(string $s) : ?bool { return filter_var($s, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); } function filterNullableBoolWithFlagsArray(string $s) : ?bool { return filter_var($s, FILTER_VALIDATE_BOOLEAN, ["flags" => FILTER_NULL_ON_FAILURE]); } function filterFloat(string $s) : float { $filtered = filter_var($s, FILTER_VALIDATE_FLOAT); if ($filtered === false) { return 0.0; } return $filtered; } function filterFloatWithDefault(string $s) : float { return filter_var($s, FILTER_VALIDATE_FLOAT, ["options" => ["default" => 5.0]]); }', ], 'callVariableVar' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' ['UndefinedConstant', 'UnresolvableInclude'], ], 'noNamespaceClash' => [ ' [ ' [ '$a' => 'int', '$b' => 'int', '$c' => 'float', '$d' => 'float', '$e' => 'int|float', ], ], 'hashInit70' => [ ' 'resource', ], [], '7.1', ], 'hashInit71' => [ ' 'resource', ], [], '7.1', ], 'hashInit72' => [ ' 'HashContext', ], [], '7.2', ], 'hashInit73' => [ ' 'HashContext', ], [], '7.3', ], 'nullableByRef' => [ ' [ '[] */ public $arr = []; } (new Props)->arr[] = get_class(new C);', ], 'getClassVariable' => [ '[] */ public $arr = []; } (new Props)->arr[] = get_class($c_instance);', ], 'getClassAnonymousNewInstance' => [ '[] */ public $arr = []; } (new Props)->arr[] = get_class(new class implements I{});', ], 'getClassAnonymousVariable' => [ '[] */ public $arr = []; } (new Props)->arr[] = get_class($anon_instance);', ], 'arrayReversePreserveNonEmptiness' => [ ' [ ' [ '$a' => 'int|false', '$b' => 'int|false', '$c' => 'int', ] ], 'PHP73-hrtime' => [ ' [ '$a' => 'int', '$b' => 'array{0:int, 1:int}', '$c' => 'array{0:int, 1:int}|int', '$d' => 'array{0:int, 1:int}', ], ], 'PHP73-hrtimeCanBeFloat' => [ ' [ ' [ '$a' => 'int', '$b' => 'int', '$c' => 'string', '$d' => 'int', '$e' => 'int', '$f' => 'int', ], ], 'minUnpackedArg' => [ ' [ '$f' => 'int', ], ], 'sscanf' => [ ' [ '$hours' => 'string|int|float', '$minutes' => 'string|int|float', '$seconds' => 'string|int|float', ], ], 'inferArrayMapReturnType' => [ ' */ function Foo(DateTime ...$dateTimes) : array { return array_map( function ($dateTime) { return (string) ($dateTime->format("c")); }, $dateTimes ); }', ], ]; } /** * @return iterable */ public function providerInvalidCodeParse() { return [ 'arrayFilterWithoutTypes' => [ ' 5, "b" => 12, "c" => null], function(?int $i) { return $_GET["a"]; } );', 'error_message' => 'MixedArgumentTypeCoercion', 'error_levels' => ['MissingClosureParamType', 'MissingClosureReturnType'], ], 'arrayFilterUseMethodOnInferrableInt' => [ 'foo(); });', 'error_message' => 'InvalidMethodCall', ], 'arrayMapUseMethodOnInferrableInt' => [ 'foo(); }, [1, 2, 3, 4]);', 'error_message' => 'InvalidMethodCall', ], 'invalidScalarArgument' => [ ' 'InvalidScalarArgument', ], 'invalidArgumentWithDeclareStrictTypes' => [ ' 'InvalidArgument', ], 'builtinFunctioninvalidArgumentWithWeakTypes' => [ ' 'InvalidScalarArgument', ], 'builtinFunctioninvalidArgumentWithDeclareStrictTypes' => [ ' 'InvalidArgument', ], 'builtinFunctioninvalidArgumentWithDeclareStrictTypesInClass' => [ ' 'InvalidArgument', ], 'mixedArgument' => [ ' 'MixedArgument', 'error_levels' => ['MixedAssignment'], ], 'nullArgument' => [ ' 'NullArgument', ], 'tooFewArguments' => [ ' 'TooFewArguments', ], 'tooManyArguments' => [ ' 'TooManyArguments - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:21 - Too many arguments for method fooFoo ' . '- expecting 1 but saw 2', ], 'tooManyArgumentsForConstructor' => [ ' 'TooManyArguments', ], 'typeCoercion' => [ ' 'ArgumentTypeCoercion', ], 'arrayTypeCoercion' => [ ' 'ArgumentTypeCoercion', ], 'duplicateParam' => [ ' 'DuplicateParam', 'error_levels' => ['MissingParamType'], ], 'invalidParamDefault' => [ ' 'InvalidParamDefault', ], 'invalidDocblockParamDefault' => [ ' 'InvalidParamDefault', ], 'badByRef' => [ ' 'InvalidPassByReference', ], 'badArrayByRef' => [ ' 'InvalidPassByReference', ], 'invalidArgAfterCallable' => [ ' 'InvalidScalarArgument', 'error_levels' => [ 'MixedAssignment', 'MixedArrayAccess', 'RedundantConditionGivenDocblockType', ], ], 'undefinedFunctionInArrayMap' => [ ' 'UndefinedFunction', ], 'arrayMapWithNonCallableStringArray' => [ ' 'InvalidArgument', ], 'arrayMapWithNonCallableIntArray' => [ ' 'InvalidArgument', ], 'objectLikeKeyChecksAgainstDifferentGeneric' => [ ' $b */ function a($b): int { return $b["a"]; } a(["a" => "hello"]);', 'error_message' => 'InvalidScalarArgument', ], 'objectLikeKeyChecksAgainstDifferentObjectLike' => [ ' "hello"]);', 'error_message' => 'InvalidArgument', ], 'possiblyNullFunctionCall' => [ ' 'PossiblyNullFunctionCall', ], 'possiblyInvalidFunctionCall' => [ ' 'PossiblyInvalidFunctionCall', ], 'arrayFilterBadArgs' => [ ' 'InvalidScalarArgument', ], 'arrayFilterTooFewArgs' => [ ' 'TooFewArguments', ], 'arrayMapBadArgs' => [ ' 'InvalidScalarArgument', ], 'arrayMapTooFewArgs' => [ ' 'TooFewArguments', ], 'arrayMapTooManyArgs' => [ ' 'TooManyArguments', ], 'varExportAssignmentToVoid' => [ ' 'AssignmentToVoid', ], 'explodeWithEmptyString' => [ ' 'FalsableReturnStatement', ], 'complainAboutArrayToIterable' => [ ' $p */ function takesIterableOfA(iterable $p): void {} takesIterableOfA([new B]); // should complain', 'error_message' => 'InvalidArgument', ], 'complainAboutArrayToIterableSingleParam' => [ ' $p */ function takesIterableOfA(iterable $p): void {} takesIterableOfA([new B]); // should complain', 'error_message' => 'InvalidArgument', ], 'putInvalidTypeMessagesFirst' => [ ' 'InvalidArgument', ], 'arrayReduceInvalidClosureTooFewArgs' => [ ' 'InvalidArgument', 'error_levels' => ['MixedTypeCoercion'], ], 'arrayReduceInvalidItemType' => [ ' 'InvalidArgument', 'error_levels' => ['MissingClosureReturnType'], ], 'arrayReduceInvalidCarryType' => [ ' 'InvalidArgument', 'error_levels' => ['MissingClosureReturnType'], ], 'arrayReduceInvalidCarryOutputType' => [ ' 'InvalidArgument', ], 'arrayPopNotNull' => [ ' $list */ function test(array $list) : void { while (!empty($list)) { $tmp = array_pop($list); if ($tmp === null) {} } }', 'error_message' => 'DocblockTypeContradiction', ], 'getTypeInvalidValue' => [ ' 'TypeDoesNotContainType', ], 'rangeWithFloatStep' => [ ' 'InvalidScalarArgument', ], 'rangeWithFloatStart' => [ ' 'InvalidScalarArgument', ], 'duplicateFunction' => [ ' 'DuplicateFunction', ], 'duplicateCoreFunction' => [ ' 'DuplicateFunction', ], 'usortInvalidComparison' => [ ' 'InvalidArgument', ], 'usortInvalidCallableString' => [ ' 'InvalidArgument', ], 'functionCallOnMixed' => [ ' 'MixedFunctionCall', ], 'iterableOfObjectCannotAcceptIterableOfInt' => [ ' $_p */ function accepts(iterable $_p): void {} /** @return iterable */ function iterable() { yield 1; } accepts(iterable());', 'error_message' => 'InvalidArgument', ], 'iterableOfObjectCannotAcceptTraversableOfInt' => [ ' $_p */ function accepts(iterable $_p): void {} /** @return Traversable */ function traversable() { yield 1; } accepts(traversable());', 'error_message' => 'InvalidArgument', ], 'iterableOfObjectCannotAcceptGeneratorOfInt' => [ ' $_p */ function accepts(iterable $_p): void {} /** @return Generator */ function generator() { yield 1; } accepts(generator());', 'error_message' => 'InvalidArgument', ], 'iterableOfObjectCannotAcceptArrayOfInt' => [ ' $_p */ function accepts(iterable $_p): void {} /** @return array */ function arr() { return [1]; } accepts(arr());', 'error_message' => 'InvalidArgument', ], 'nonNullableByRef' => [ ' 'NullReference', ], 'intCastByRef' => [ '