,error_levels?:string[]}> */ public function providerValidCodeParse() { return [ 'validTemplatedType' => [ ' [ ' [ ' [ 'foo("string"));', ], 'genericArrayKeys' => [ ' $arr * @return array */ function my_array_keys($arr) { return array_keys($arr); } $a = my_array_keys(["hello" => 5, "goodbye" => new \Exception()]);', 'assertions' => [ '$a' => 'array', ], ], 'genericArrayFlip' => [ ' $arr * @return array */ function my_array_flip($arr) { return array_flip($arr); } $b = my_array_flip(["hello" => 5, "goodbye" => 6]);', 'assertions' => [ '$b' => 'array', ], ], 'byRefKeyValueArray' => [ ' $arr */ function byRef(array &$arr) : void {} $b = ["a" => 5, "c" => 6]; byRef($b);', 'assertions' => [ '$b' => 'array', ], ], 'byRefMixedKeyArray' => [ ' $arr */ function byRef(array &$arr) : void {} $b = ["a" => 5, "c" => 6]; byRef($b);', 'assertions' => [ '$b' => 'array', ], ], 'mixedArrayPop' => [ ' $arr * @return TValue|null */ function my_array_pop(array &$arr) { return array_pop($arr); } /** @var mixed */ $b = ["a" => 5, "c" => 6]; $a = my_array_pop($b);', 'assertions' => [ '$a' => 'mixed', '$b' => 'array', ], 'error_levels' => ['MixedAssignment', 'MixedArgument'], ], 'genericArrayPop' => [ ' $arr * @return TValue|null */ function my_array_pop(array &$arr) { return array_pop($arr); } $b = ["a" => 5, "c" => 6]; $a = my_array_pop($b);', 'assertions' => [ '$a' => 'int|null', '$b' => 'array', ], ], 'templateCallableReturnType' => [ ' [ ' [ ' $t * @return array */ function f(Traversable $t): array { $ret = []; foreach ($t as $k => $v) $ret[$k] = $v; return $ret; } /** @return Generator */ function g():Generator { yield new stdClass; } takesArrayOfStdClass(f(g())); /** @param array $p */ function takesArrayOfStdClass(array $p): void {}', ], 'splatTemplateParam' => [ ' $arr * @param array $arr2 * @return array */ function splat_proof(array $arr, array $arr2) { return $arr; } $foo = [ [1, 2, 3], [1, 2], ]; $a = splat_proof(...$foo);', 'assertions' => [ '$a' => 'array', ], ], 'passArrayByRef' => [ ' $_arr * @return null|TValue * @psalm-ignore-nullable-return */ function fRef(array &$_arr) { return array_shift($_arr); } /** * @template TKey as array-key * @template TValue * * @param array $_arr * @return null|TValue * @psalm-ignore-nullable-return */ function fNoRef(array $_arr) { return array_shift($_arr); }', ], 'classTemplateAsCorrect' => [ ' [ ' [ ' [ 'bar(); /** @var T&D */ $b = $some_t; $b->bar(); /** @var D&T */ $b = $some_t; $b->bar(); return $a; }', 'assertions' => [], 'error_levels' => ['MixedAssignment', 'MissingParamType'], ], 'bindFirstTemplatedClosureParameterValid' => [ ' [ ' [ ' [ 'getIterator();', [ '$i' => 'Traversable', ], ], 'upcastArrayToIterable' => [ ' $collection * @return V * @psalm-suppress InvalidReturnType */ function first($collection) {} $one = first([1,2,3]);', [ '$one' => 'int', ], ], 'templateIntersectionLeft' => [ ' [ ' [ '|TReturn) $gen * @return array */ function call(callable $gen) : array { $return = $gen(); if ($return instanceof Generator) { return [$gen->getReturn()]; } /** @var array */ $wrapped_gen = [$gen]; return $wrapped_gen; } $arr = call( /** * @return Generator */ function() { yield 1; return "hello"; } );', [ '$arr' => 'array', ], ], 'templateOfWithSpace' => [ ' */ class Foo { } /** * @param Foo> $a */ function bar(Foo $a) : void {}', ], 'allowUnionTypeParam' => [ ' $y */ function example($x, $y): void {} example( /** * @param int|false $x */ function($x): void {}, [strpos("str", "str")] );', ], 'ignoreTooManyArrayArgs' => [ ' */ $b = [1, 2, 3]; takesArray($b);', ], 'functionTemplateUnionType' => [ ' [ '$s' => 'string', '$i' => 'int', ], ], 'reconcileTraversableTemplatedAndNormal' => [ 'getIterator(); $t = $a; } if (!$t instanceof Iterator) { return; } if (rand(0, 1) && rand(0, 1)) { $t->next(); } }', ], 'keyOfTemplate' => [ ' * * @param T $o * @param K $name * * @return T[K] */ function getOffset(array $o, $name) { return $o[$name]; } $a = ["foo" => "hello", "bar" => 2]; $b = getOffset($a, "foo"); $c = getOffset($a, "bar");', [ '$b' => 'string', '$c' => 'int', ], ], 'dontGeneraliseBoundParamWithWiderCallable' => [ ' 'C', ], ], 'allowTemplateTypeBeingUsedInsideFunction' => [ ' [ ' [ ' [ ' [ 'id = $id; } public static function fromString(string $id): self { return new self($id); } } /** * @template T * @psalm-param callable(string): T $generator * @psalm-return callable(): T */ function idGenerator(callable $generator) { return static function () use ($generator) { return $generator("random id"); }; } function client(Id $id): void { } $staticIdGenerator = idGenerator([Id::class, "fromString"]); client($staticIdGenerator());' ], 'noCrashWhenTemplatedClassIsStatic' => [ 'newInstance(); }' ], ]; } /** * @return iterable */ public function providerInvalidCodeParse() { return [ 'invalidTemplatedType' => [ ' 'InvalidScalarArgument', ], 'invalidTemplatedStaticMethodType' => [ ' 'InvalidScalarArgument', ], 'invalidTemplatedInstanceMethodType' => [ 'foo(4));', 'error_message' => 'InvalidScalarArgument', ], 'replaceChildTypeNoHint' => [ ' $t * @return array */ function f(Traversable $t): array { $ret = []; foreach ($t as $k => $v) $ret[$k] = $v; return $ret; } function g():Generator { yield new stdClass; } takesArrayOfStdClass(f(g())); /** @param array $p */ function takesArrayOfStdClass(array $p): void {}', 'error_message' => 'MixedArgumentTypeCoercion', ], 'classTemplateAsIncorrectClass' => [ ' 'InvalidArgument', ], 'classTemplateAsIncorrectInterface' => [ ' 'InvalidArgument', ], 'templateFunctionMethodCallWithoutMethod' => [ 'bar(); }', 'error_message' => 'PossiblyUndefinedMethod', ], 'templateFunctionMethodCallWithoutAsType' => [ 'bar(); }', 'error_message' => 'MixedMethodCall', ], 'bindFirstTemplatedClosureParameterInvalidScalar' => [ ' 'InvalidScalarArgument', ], 'bindFirstTemplatedClosureParameterTypeCoercion' => [ ' 'ArgumentTypeCoercion', ], 'callableDoesNotReturnItself' => [ ' 'InvalidScalarArgument', ], 'multipleArgConstraintWithMoreRestrictiveFirstArg' => [ ' 'ArgumentTypeCoercion', ], 'multipleArgConstraintWithMoreRestrictiveSecondArg' => [ ' 'ArgumentTypeCoercion', ], 'multipleArgConstraintWithLessRestrictiveThirdArg' => [ ' 'ArgumentTypeCoercion', ], 'possiblyInvalidArgumentWithUnionFirstArg' => [ ' 'PossiblyInvalidArgument', ], 'possiblyInvalidArgumentWithUnionSecondArg' => [ ' 'PossiblyInvalidArgument', ], 'preventTemplateTypeAsBeingUsedInsideFunction' => [ ' 'InvalidArgument', ], 'preventWrongTemplateBeingPassed' => [ ' 'InvalidArgument', ], 'preventTemplateTypeReturnMoreGeneral' => [ ' 'InvalidReturnStatement', ], 'preventReturningString' => [ ' 'InvalidReturnStatement', ], 'unTemplatedVarOnReturn' => [ ' 'InvalidReturnStatement', ], 'templateReturnTypeOfCallableWithIncompatibleType' => [ ' 'InvalidArgument', ], 'templateInvokeArg' => [ ' 'InvalidArgument' ], ]; } }