[ 'code' => ' [ 'code' => ' [ 'code' => ' $a . "blah", $bar );', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '7.4', ], 'varReturnType' => [ 'code' => ' [ '$a' => 'int', ], ], 'varReturnTypeArray' => [ 'code' => ' $a + 1; $a = $add_one(1);', 'assertions' => [ '$a' => 'int', ], 'ignored_issues' => [], 'php_version' => '7.4', ], 'correctParamType' => [ 'code' => ' [ 'code' => ' [ '$a' => 'list{int, int, int}', ], ], 'inlineCallableFunction' => [ 'code' => ' $b ? 1 : 0; } $arr = [5, 4, 3, 1, 2]; usort($arr, "fooBar"); } }', ], 'closureSelf' => [ 'code' => 'subitems = array_map( function(self $i): self { return $i; }, $in ); } } new A([new A, new A]);', ], 'arrayMapVariadicClosureArg' => [ 'code' => ' [ 'code' => ' [ 'code' => ' $f($g($x)); }', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '7.4', ], 'returnsTypedClosureWithClasses' => [ 'code' => ' [ '$a' => 'A', ], ], 'returnsTypedClosureWithSubclassParam' => [ 'code' => ' [ '$a' => 'A', ], ], 'returnsTypedClosureWithParentReturn' => [ 'code' => ' [ '$a' => 'A', ], ], 'inferArrayMapReturnTypeWithoutTypehints' => [ 'code' => ' [], 'ignored_issues' => ['MissingClosureReturnType'], ], 'inferArrayMapReturnTypeWithTypehints' => [ 'code' => ' [ 'code' => 'invokable = $invokable; } public function callTheInvokableDirectly(): bool { return ($this->invokable)(); } public function callTheInvokableIndirectly(): bool { $r = $this->invokable; return $r(); } }', ], 'mirrorCallableParams' => [ 'code' => ' 0; }));', ], 'singleLineClosures' => [ 'code' => ' [ '$a' => 'pure-Closure():pure-Closure():string', '$b' => 'string', ], ], 'voidReturningArrayMap' => [ 'code' => ' [ 'code' => ' 0; } } acceptsIntToBool(Closure::fromCallable(new NamedInvokable));', ], 'closureFromCallableInvokableAnonymousClass' => [ 'code' => ' 0; } }; acceptsIntToBool(Closure::fromCallable($anonInvokable));', ], 'publicCallableFromInside' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ '$closure' => 'pure-Closure(string):int<0, max>', ], ], 'allowClosureWithNarrowerReturn' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => 'a = fn(int $a) : int => $a + 5; } public function invoker(int $b) : int { return $this->a->__invoke($b); } }', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '7.4', ], 'annotateShortClosureReturn' => [ 'code' => ' /** @var bool */ returnsBool();', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '7.4', ], 'rememberParentAssertions' => [ 'code' => 'a instanceof A) { function () use ($a): void { $a->a->foo(); }; } }', ], 'CallableWithArrayMap' => [ 'code' => ' $className * @return callable(...mixed):T */ function maker(string $className) { return function(...$args) use ($className) { /** @psalm-suppress MixedMethodCall */ return new $className(...$args); }; } $maker = maker(stdClass::class); $result = array_map($maker, ["abc"]);', 'assertions' => [ '$result' => 'list{stdClass}', ], ], 'CallableWithArrayReduce' => [ 'code' => ' [ '$result' => 'int', ], ], 'FirstClassCallable:NamedFunction:is_int' => [ 'code' => ' [ '$closure' => 'pure-Closure(mixed):bool', '$result' => 'bool', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:NamedFunction:strlen' => [ 'code' => ' [ '$closure' => 'pure-Closure(string):int<0, max>', '$result' => 'int<0, max>', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:InstanceMethod:UserDefined' => [ 'code' => 'string); } } $test = new Test("test"); $closure = $test->length(...); $length = $closure(); ', 'assertions' => [ '$length' => 'int', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:InstanceMethod:Expr' => [ 'code' => 'string); } } $test = new Test("test"); $method_name = "length"; $closure = $test->$method_name(...); $length = $closure(); ', 'assertions' => [ '$length' => 'int', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:InstanceMethod:BuiltIn' => [ 'code' => 'count(...); $count = $closure(); ', 'assertions' => [ '$count' => 'int', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:StaticMethod' => [ 'code' => ' [ '$length' => 'int', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:StaticMethod:Expr' => [ 'code' => ' [ '$length' => 'int', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:InvokableObject' => [ 'code' => ' [ '$length' => 'int', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:FromClosure' => [ 'code' => ' strlen($string); $closure = $closure(...); ', 'assertions' => [ '$closure' => 'pure-Closure(string):int<0, max>', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:MagicInstanceMethod' => [ 'code' => ' strlen($this->string), default => throw new \Error("Undefined method"), }; } } $test = new Test("test"); $closure = $test->length(...); $length = $closure(); ', 'assertions' => [ '$length' => 'int', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:MagicStaticMethod' => [ 'code' => ' strlen((string) $args[0]), default => throw new \Error("Undefined method"), }; } } $closure = Test::length(...); $length = $closure("test"); ', 'assertions' => [ '$length' => 'int', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:InheritedStaticMethod' => [ 'code' => ' [], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:InheritedStaticMethodWithStaticTypeParameter' => [ 'code' => ' */ public static function create(int $i): Holder { return new Holder(new static($i)); } } class C extends A {} /** @param \Closure(int):Holder $_ */ function takesIntToHolder(\Closure $_): void {} takesIntToHolder(C::create(...));', ], 'FirstClassCallable:WithArrayMap' => [ 'code' => ' $value * $value; $result1 = array_map((new \SplQueue())->enqueue(...), $array); $result2 = array_map(strval(...), $array); $result3 = array_map($closure(...), $array); ', 'assertions' => [ '$result1' => 'list{null, null, null}', '$result2' => 'list{string, string, string}', '$result3' => 'list{int, int, int}', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:array_map' => [ 'code' => ' [], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:AssignmentVisitorMap' => [ 'code' => ' */ public array $handlers = []; public function register(): void { foreach ([1, 2, 3] as $index) { $this->push($this->handler(...)); } } /** * @param Closure():void $closure * @return void */ private function push(\Closure $closure): void { $this->handlers[] = $closure; } private function handler(): void { } } $test = new Test(); $test->register(); $handlers = $test->handlers; ', 'assertions' => [ '$handlers' => 'list', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:Method:Asserted' => [ 'code' => '$m(...); } ', 'assertions' => [ '$r===' => 'Closure|false', ], 'ignored_issues' => [], 'php_version' => '8.1', ], 'arrowFunctionReturnsNeverImplictly' => [ 'code' => ' throw new Exception($a), $bar );', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '8.1', ], 'arrowFunctionReturnsNeverExplictly' => [ 'code' => ' die(), $bar );', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '8.1', ], 'unknownFirstClassCallable' => [ 'code' => ' [ 'code' => ' [ 'code' => <<<'PHP' Foo::bar(23, []); } PHP, 'assertions' => [], 'ignored_issues' => [], 'php_version' => '7.4', ], 'classExistsInOuterScopeOfAClosure' => [ 'code' => <<<'PHP' [ 'code' => ' 'InvalidScalarArgument', ], 'noReturn' => [ 'code' => ' 'InvalidReturnType', ], 'possiblyNullFunctionCall' => [ 'code' => ' 'MixedReturnStatement', ], 'wrongParamType' => [ 'code' => ' 'InvalidScalarArgument', ], 'missingClosureReturnType' => [ 'code' => ' 'MissingClosureReturnType', ], 'returnsTypedClosureWithBadReturnType' => [ 'code' => ' 'InvalidReturnStatement', ], 'returnsTypedCallableWithBadReturnType' => [ 'code' => ' 'InvalidReturnStatement', ], 'returnsTypedClosureWithBadParamType' => [ 'code' => ' 'InvalidReturnStatement', ], 'returnsTypedCallableWithBadParamType' => [ 'code' => ' 'InvalidReturnStatement', ], 'returnsTypedClosureWithBadCall' => [ 'code' => ' 'InvalidArgument', ], 'returnsTypedClosureWithSubclassParam' => [ 'code' => ' 'LessSpecificReturnStatement', ], 'returnsTypedClosureWithSubclassReturn' => [ 'code' => ' 'LessSpecificReturnStatement', ], 'returnsTypedClosureFromCallable' => [ 'code' => ' 'LessSpecificReturnStatement', ], 'undefinedVariable' => [ 'code' => ' 'UndefinedVariable', ], 'voidReturningArrayMap' => [ 'code' => ' 'TypeDoesNotContainType', ], 'closureFromCallableInvokableNamedClassWrongArgs' => [ 'code' => ' 0; } } acceptsIntToBool(Closure::fromCallable(new NamedInvokable));', 'error_message' => 'InvalidScalarArgument', ], 'undefinedClassForCallable' => [ 'code' => ' 'UndefinedClass', ], 'useDuplicateName' => [ 'code' => ' 'DuplicateParam', ], 'privateCallable' => [ 'code' => ' 'InvalidArgument', ], 'prohibitCallableWithRequiredArg' => [ 'code' => ' 'InvalidArgument', ], 'useClosureDocblockType' => [ 'code' => ' 'ArgumentTypeCoercion - src' . DIRECTORY_SEPARATOR . 'somefile.php:13:28 - Argument 1 of takesB expects B, but parent type A provided', ], 'closureByRefUseToMixed' => [ 'code' => ' 'MixedReturnStatement', ], 'noCrashWhenComparingIllegitimateCallable' => [ 'code' => ' ""; }', 'error_message' => 'InvalidReturnStatement', 'ignored_issues' => [], 'php_version' => '7.4', ], 'detectImplicitVoidReturn' => [ 'code' => 'getMessage(); } takesClosureReturningException( function () { echo "hello"; } );', 'error_message' => 'InvalidArgument', ], 'undefinedVariableInEncapsedString' => [ 'code' => ' "$a"; ', 'error_message' => 'UndefinedVariable', 'ignored_issues' => [], 'php_version' => '7.4', ], 'undefinedVariableInStringCast' => [ 'code' => ' (string) $a; ', 'error_message' => 'UndefinedVariable', 'ignored_issues' => [], 'php_version' => '7.4', ], 'forbidTemplateAnnotationOnClosure' => [ 'code' => ' 'InvalidDocblock', ], 'forbidTemplateAnnotationOnShortClosure' => [ 'code' => ' false; ', 'error_message' => 'InvalidDocblock', 'ignored_issues' => [], 'php_version' => '7.4', ], 'closureInvalidArg' => [ 'code' => ' 'InvalidArgument', ], 'FirstClassCallable:UndefinedMethod' => [ 'code' => 'undefined(...); $count = $closure(); ', 'error_message' => 'UndefinedMethod', 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:UndefinedMagicInstanceMethod' => [ 'code' => ' throw new \Error("Undefined method"), }; } } $test = new Test(); $closure = $test->length(...); $length = $closure(); ', 'error_message' => 'UndefinedMagicMethod', 'ignored_issues' => [], 'php_version' => '8.1', ], 'FirstClassCallable:UndefinedMagicStaticMethod' => [ 'code' => ' throw new \Error("Undefined method"), }; } } $closure = Test::length(...); $length = $closure(); ', 'error_message' => 'MixedAssignment', 'ignored_issues' => [], 'php_version' => '8.1', ], 'thisInStaticClosure' => [ 'code' => 'a; }; $f(); } } ', 'error_message' => 'InvalidScope', ], 'thisInStaticArrowFunction' => [ 'code' => ' $this->a; return $f();; } } ', 'error_message' => 'InvalidScope', 'ignored_issues' => [], 'php_version' => '7.4', ], 'FirstClassCallable:WithNew' => [ 'code' => <<<'PHP' 'ParseError', 'ignored_issues' => [], 'php_version' => '8.1', ], ]; } }