,error_levels?:string[]}> */ public function providerValidCodeParse(): iterable { return [ 'byRefUseVar' => [ ' [ ' [ ' $a . "blah", $bar );', 'assertions' => [], 'error_levels' => [], '7.4', ], 'inferArgFromClassContext' => [ ' $a + $b);', 'assertions' => [ '$a' => 'int', ], 'error_levels' => [], '7.4', ], 'inferArgFromClassContextWithNamedArguments' => [ ' $_a + $_b, bar: fn($_a, $_b) => $_a + $_b, );', 'assertions' => [ '$a' => 'int', ], 'error_levels' => [], '7.4', ], 'inferArgFromClassContextInGenericContext' => [ ' */ public function map(Closure $ab): ArrayList { throw new RuntimeException("???"); } } /** * @template T * @param ArrayList $list * @return ArrayList */ function asTupled(ArrayList $list): ArrayList { return $list->map(function ($_a) { return [$_a]; }); } /** @var ArrayList $a */ $a = new ArrayList(); $b = asTupled($a);', 'assertions' => [ '$b' => 'ArrayList', ], ], 'inferArgByPreviousFunctionArg' => [ ' $_collection * @param callable(A): B $_ab * @return list */ function map(iterable $_collection, callable $_ab) { return []; } /** @template T */ final class Foo { /** @return Foo */ public function toInt() { throw new RuntimeException("???"); } } /** @var list> */ $items = []; $inferred = map($items, function ($i) { return $i->toInt(); });', 'assertions' => [ '$inferred' => 'list>', ], ], 'inferTemplateForExplicitlyTypedArgByPreviousFunctionArg' => [ ' $_collection * @param callable(A): B $_ab * @return list */ function map(iterable $_collection, callable $_ab) { return []; } /** @template T */ final class Foo { /** @return Foo */ public function toInt() { throw new RuntimeException("???"); } } /** @var list> */ $items = []; $inferred = map($items, function (Foo $i) { return $i->toInt(); });', 'assertions' => [ '$inferred' => 'list>', ], ], 'doNotInferTemplateForExplicitlyTypedWithPhpdocArgByPreviousFunctionArg' => [ ' $_collection * @param callable(A): B $_ab * @return list */ function map(iterable $_collection, callable $_ab) { return []; } /** @template T */ final class Foo { } /** @var list> */ $items = []; $inferred = map($items, /** @param Foo $i */ function ($i) { return $i; } );', 'assertions' => [ '$inferred' => 'list', ], ], 'varReturnType' => [ ' [ '$a' => 'int', ], ], 'varReturnTypeArray' => [ ' $a + 1; $a = $add_one(1);', 'assertions' => [ '$a' => 'int', ], 'error_levels' => [], '7.4', ], 'varCallableParamReturnType' => [ ' [ ' [ ' $a . "blah"; }', 'assertions' => [], 'error_levels' => [], '7.4', ], 'callable' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ '$a' => 'array{string, string}', '$b' => 'array{string, string}', '$c' => 'array{string, string}', '$d' => 'array{string, string}', '$e' => 'array{string, string}', '$f' => 'array{string, string}', ], ], 'arrayCallableMethod' => [ ' [ ' [ ' $b ? 1 : 0; } $arr = [5, 4, 3, 1, 2]; usort($arr, "fooBar"); } }', ], 'closureSelf' => [ 'subitems = array_map( function(self $i): self { return $i; }, $in ); } } new A([new A, new A]);', ], 'possiblyUndefinedFunction' => [ ' [ ' [ ' [ 'callMeMaybe("foo");', ], 'isCallableString' => [ ' [ ' [ 'callable = $callable; } public function callTheCallableDirectly(): bool { return ($this->callable)(); } public function callTheCallableIndirectly(): bool { $r = $this->callable; return $r(); } }', ], 'invokableProperties' => [ 'invokable = $invokable; } public function callTheInvokableDirectly(): bool { return ($this->invokable)(); } public function callTheInvokableIndirectly(): bool { $r = $this->invokable; return $r(); } }', ], 'nullableReturnTypeShorthand' => [ ' [ ' [ ' [ ' [ ' $b; } } f("strcmp"); f([new C, "m"]); f([C::class, "m"]);', ], 'callableWithSpaces' => [ ' [ ' [ 'func2(function(B $x): void {}); $c->func2(function(B $x): void {}); class A {} class B extends A { /** * @param callable(self) $f */ function func2(callable $f): void { $f($this); } }', ], 'callableParentArg' => [ 'func3(function(A $x): void {}); $c->func3(function(A $x): void {}); class A {} class B extends A { /** * @param callable(parent) $f */ function func3(callable $f): void { $f($this); } }', ], 'callableStaticArg' => [ 'func1(function(B $x): void {}); $c->func1(function(C $x): void {}); class A {} class B extends A { /** * @param callable(static) $f */ function func1(callable $f): void { $f($this); } }', ], 'callableStaticReturn' => [ 'func1(function(): C { return new C(); });', ], 'callableSelfReturn' => [ 'func2(function() { return new B(); }); $c->func2(function() { return new C(); });', ], 'callableParentReturn' => [ 'func3(function() { return new A(); });', ], 'selfArrayMapCallableWrongClass' => [ ' */ public function bar() { return array_map([Foo::class, "foo"], [1,2,3]); } /** @return array */ public function bat() { return array_map([Foo::class, "baz"], [1]); } }', ], 'dynamicCallableArray' => [ 'value = $value; } }', ], 'callableIsArrayAssertion' => [ ' [ ' [ ' [ ' [ ' [ ' [ 'id = $id; } /** * @return static */ final public static function fromString(string $id): self { return new static($id); } } final class CriterionId extends Id { } final class CriterionIds { /** * @psalm-var non-empty-list */ private $ids; /** * @psalm-param non-empty-list $ids */ private function __construct(array $ids) { $this->ids = $ids; } /** * @psalm-param non-empty-list $ids */ public static function fromStrings(array $ids): self { return new self(array_map([CriterionId::class, "fromString"], $ids)); } }' ], 'offsetOnCallable' => [ ' [ ' 'class-string|object', '$method' => 'string' ] ], 'callableInterface' => [ ' [ ' "ASC", "start_time" => "ASC"]);' ], 'callOnInvokableOrCallable' => [ ' [ 'takesACall(function() {return $this;}); } }' ], 'returnClosureReturningStatic' => [ ' [ ' [ ' [ ' [ ' */ public function providerInvalidCodeParse(): iterable { return [ 'undefinedCallableClass' => [ 'getFoo()($argOne, $argTwo); } }', 'error_message' => 'InvalidFunctionCall', 'error_levels' => ['UndefinedClass', 'MixedInferredReturnType'], ], 'undefinedCallableMethodFullString' => [ ' 'UndefinedMethod', ], 'undefinedCallableMethodClassConcat' => [ ' 'UndefinedMethod', ], 'undefinedCallableMethodArray' => [ ' 'InvalidArgument', ], 'undefinedCallableMethodArrayWithoutClass' => [ ' 'InvalidArgument', ], 'undefinedCallableMethodClass' => [ ' 'UndefinedClass', ], 'undefinedCallableFunction' => [ ' 'UndefinedFunction', ], 'stringFunctionCall' => [ ' 'MixedAssignment', ], 'wrongCallableReturnType' => [ ' 'InvalidReturnStatement', ], 'checkCallableTypeString' => [ ' 'InvalidScalarArgument', ], 'checkCallableTypeArrayInstanceFirstArg' => [ ' $b; } } f([new C, "m"]);', 'error_message' => 'InvalidScalarArgument', ], 'checkCallableTypeArrayClassStringFirstArg' => [ ' $b; } } f([C::class, "m"]);', 'error_message' => 'InvalidScalarArgument', ], 'callableWithSpaceAfterColonBadVarArg' => [ 'p = function (string $s, string $t): stdClass { return new stdClass; }; } }', 'error_message' => 'InvalidPropertyAssignmentValue', ], 'callableWithSpaceBeforeColonBadVarArg' => [ 'p = function (string $s, string $t): stdClass { return new stdClass; }; } }', 'error_message' => 'InvalidPropertyAssignmentValue', ], 'callableWithSpacesEitherSideOfColonBadVarArg' => [ 'p = function (string $s, string $t): stdClass { return new stdClass; }; } }', 'error_message' => 'InvalidPropertyAssignmentValue', ], 'badArrayMapArrayCallable' => [ ' 'InvalidArgument', ], 'noFatalErrorOnMissingClassWithSlash' => [ ' 'InvalidArgument' ], 'noFatalErrorOnMissingClassWithoutSlash' => [ ' 'InvalidArgument' ], 'preventStringDocblockType' => [ ' 'MismatchingDocblockParamType', ], 'moreSpecificCallable' => [ ' 'MixedArgumentTypeCoercion' ], 'undefinedVarInBareCallable' => [ ' 'UndefinedVariable', ], 'dontQualifyStringCallables' => [ ' 'UndefinedFunction', ], 'badCustomFunction' => [ ' 'InvalidScalarArgument', ], 'emptyCallable' => [ ' 'InvalidFunctionCall', ], 'ImpureFunctionCall' => [ ' $values * @psalm-param (callable(T): numeric) $num_func * * @psalm-return null|T * * @psalm-pure */ function max_by(array $values, callable $num_func) { $max = null; $max_num = null; foreach ($values as $value) { $value_num = $num_func($value); if (null === $max_num || $value_num >= $max_num) { $max = $value; $max_num = $value_num; } } return $max; } $c = max_by([1, 2, 3], static function(int $a): int { return $a + mt_rand(0, $a); }); echo $c; ', 'error_message' => 'ImpureFunctionCall', 'error_levels' => [], ], 'constructCallableFromClassStringArray' => [ ' $c */ function foo(string $c) : void { takesCallableReturningString([$c, "bar"]); }', 'error_message' => 'InvalidArgument', ], 'inexistantCallableinCallableString' => [ ' 'InvalidArgument', ], 'mismatchParamTypeFromDocblock' => [ ' */ public function map(Closure $effect): ArrayList { throw new RuntimeException("???"); } } /** * @template T * @template B * * @param ArrayList $list * @return ArrayList */ function genericContext(ArrayList $list): ArrayList { return $list->map( /** @param B $_a */ function ($_a) { return [$_a]; } ); }', 'error_message' => 'InvalidArgument', ] ]; } }