diff --git a/src/Utils/Arrays.php b/src/Utils/Arrays.php index 51916a60..8c688429 100644 --- a/src/Utils/Arrays.php +++ b/src/Utils/Arrays.php @@ -130,6 +130,8 @@ public static function contains(array $array, mixed $value): bool * @param array $array * @param ?callable(V, K, array): bool $predicate * @param ?callable(): E $else + * @param-immediately-invoked-callable $predicate + * @param-immediately-invoked-callable $else * @return ($else is null ? ?V : V|E) */ public static function first(array $array, ?callable $predicate = null, ?callable $else = null): mixed @@ -149,6 +151,8 @@ public static function first(array $array, ?callable $predicate = null, ?callabl * @param array $array * @param ?callable(V, K, array): bool $predicate * @param ?callable(): E $else + * @param-immediately-invoked-callable $predicate + * @param-immediately-invoked-callable $else * @return ($else is null ? ?V : V|E) */ public static function last(array $array, ?callable $predicate = null, ?callable $else = null): mixed @@ -166,6 +170,7 @@ public static function last(array $array, ?callable $predicate = null, ?callable * @template V * @param array $array * @param ?callable(V, K, array): bool $predicate + * @param-immediately-invoked-callable $predicate * @return ?K */ public static function firstKey(array $array, ?callable $predicate = null): int|string|null @@ -188,6 +193,7 @@ public static function firstKey(array $array, ?callable $predicate = null): int| * @template V * @param array $array * @param ?callable(V, K, array): bool $predicate + * @param-immediately-invoked-callable $predicate * @return ?K */ public static function lastKey(array $array, ?callable $predicate = null): int|string|null @@ -400,6 +406,7 @@ public static function pick(array &$array, string|int $key, mixed $default = nul * @template V * @param array $array * @param callable(V, K, array): bool $predicate + * @param-immediately-invoked-callable $predicate */ public static function some(iterable $array, callable $predicate): bool { @@ -419,6 +426,7 @@ public static function some(iterable $array, callable $predicate): bool * @template V * @param array $array * @param callable(V, K, array): bool $predicate + * @param-immediately-invoked-callable $predicate */ public static function every(iterable $array, callable $predicate): bool { @@ -438,6 +446,7 @@ public static function every(iterable $array, callable $predicate): bool * @template V * @param array $array * @param callable(V, K, array): bool $predicate + * @param-immediately-invoked-callable $predicate * @return array */ public static function filter(array $array, callable $predicate): array @@ -459,6 +468,7 @@ public static function filter(array $array, callable $predicate): array * @template R * @param array $array * @param callable(V, K, array): R $transformer + * @param-immediately-invoked-callable $transformer * @return array */ public static function map(iterable $array, callable $transformer): array @@ -481,6 +491,7 @@ public static function map(iterable $array, callable $transformer): array * @template ResV * @param array $array * @param callable(V, K, array): ?array{ResK, ResV} $transformer + * @param-immediately-invoked-callable $transformer * @return array */ public static function mapWithKeys(array $array, callable $transformer): array diff --git a/src/Utils/Callback.php b/src/Utils/Callback.php index f880f30c..f3aa2248 100644 --- a/src/Utils/Callback.php +++ b/src/Utils/Callback.php @@ -23,6 +23,7 @@ final class Callback * @param callable-string $function * @param list $args * @param callable(string, int): (bool|void|null) $onError + * @param-immediately-invoked-callable $onError */ public static function invokeSafe(string $function, array $args, callable $onError): mixed { diff --git a/src/Utils/Helpers.php b/src/Utils/Helpers.php index 95d51b95..206ce97a 100644 --- a/src/Utils/Helpers.php +++ b/src/Utils/Helpers.php @@ -23,6 +23,7 @@ class Helpers /** * Executes a callback and returns the captured output as a string. * @param callable(): void $func + * @param-immediately-invoked-callable $func */ public static function capture(callable $func): string { diff --git a/src/Utils/Iterables.php b/src/Utils/Iterables.php index e891bef3..f410e01c 100644 --- a/src/Utils/Iterables.php +++ b/src/Utils/Iterables.php @@ -56,6 +56,8 @@ public static function containsKey(iterable $iterable, mixed $key): bool * @param iterable $iterable * @param ?callable(V, K, iterable): bool $predicate * @param ?callable(): E $else + * @param-immediately-invoked-callable $predicate + * @param-immediately-invoked-callable $else * @return ($else is null ? ?V : V|E) */ public static function first(iterable $iterable, ?callable $predicate = null, ?callable $else = null): mixed @@ -77,6 +79,8 @@ public static function first(iterable $iterable, ?callable $predicate = null, ?c * @param iterable $iterable * @param ?callable(V, K, iterable): bool $predicate * @param ?callable(): E $else + * @param-immediately-invoked-callable $predicate + * @param-immediately-invoked-callable $else * @return ($else is null ? ?K : K|E) */ public static function firstKey(iterable $iterable, ?callable $predicate = null, ?callable $else = null): mixed @@ -96,6 +100,7 @@ public static function firstKey(iterable $iterable, ?callable $predicate = null, * @template V * @param iterable $iterable * @param callable(V, K, iterable): bool $predicate + * @param-immediately-invoked-callable $predicate */ public static function some(iterable $iterable, callable $predicate): bool { @@ -114,6 +119,7 @@ public static function some(iterable $iterable, callable $predicate): bool * @template V * @param iterable $iterable * @param callable(V, K, iterable): bool $predicate + * @param-immediately-invoked-callable $predicate */ public static function every(iterable $iterable, callable $predicate): bool { diff --git a/src/Utils/Process.php b/src/Utils/Process.php index 6a783140..b114f301 100644 --- a/src/Utils/Process.php +++ b/src/Utils/Process.php @@ -204,6 +204,7 @@ public function isRunning(): bool * continuously and kept in memory; an optional callback is invoked with each new output/error chunk. * * @param (\Closure(string, string): void)|null $callback + * @param-immediately-invoked-callable $callback */ public function wait(?\Closure $callback = null): void { diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index d12d042e..88570687 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -654,6 +654,7 @@ public static function matchAll( /** * Replaces all matches of the regular expression $pattern, which can be a string or array in the form `pattern => replacement`. * @param string|array $pattern + * @param-immediately-invoked-callable $replacement */ public static function replace( string $subject, diff --git a/tests/types/TypesTest.phpt b/tests/types/TypesTest.phpt index 67465c7a..d565a77a 100644 --- a/tests/types/TypesTest.phpt +++ b/tests/types/TypesTest.phpt @@ -5,3 +5,12 @@ require __DIR__ . '/../bootstrap.php'; use Nette\PHPStan\Tester\TypeAssert; TypeAssert::assertTypes(__DIR__ . '/utils-types.php'); + +TypeAssert::assertErrors( + __DIR__ . '/immediately-invoked-callables.php', + array_map( + fn(int $line) => "missingType.checkedException on line $line", + [13, 14, 20, 21, 27, 33, 39, 45, 51, 57, 64, 75, 76, 82, 83, 89, 95, 101, 107, 113, 119], + ), + [__DIR__ . '/immediately-invoked-callables.neon'], +); diff --git a/tests/types/immediately-invoked-callables.neon b/tests/types/immediately-invoked-callables.neon new file mode 100644 index 00000000..fb1c52d1 --- /dev/null +++ b/tests/types/immediately-invoked-callables.neon @@ -0,0 +1,4 @@ +parameters: + exceptions: + check: + missingCheckedExceptionInThrows: true diff --git a/tests/types/immediately-invoked-callables.php b/tests/types/immediately-invoked-callables.php new file mode 100644 index 00000000..714677db --- /dev/null +++ b/tests/types/immediately-invoked-callables.php @@ -0,0 +1,133 @@ + throw new Exception); + Arrays::first([1], null, fn() => throw new Exception); +} + + +function testArraysLast(): void +{ + Arrays::last([1], fn() => throw new Exception); + Arrays::last([1], null, fn() => throw new Exception); +} + + +function testArraysFirstKey(): void +{ + Arrays::firstKey([1], fn() => throw new Exception); +} + + +function testArraysLastKey(): void +{ + Arrays::lastKey([1], fn() => throw new Exception); +} + + +function testArraysSome(): void +{ + Arrays::some([1], fn() => throw new Exception); +} + + +function testArraysEvery(): void +{ + Arrays::every([1], fn() => throw new Exception); +} + + +function testArraysFilter(): void +{ + Arrays::filter([1], fn() => throw new Exception); +} + + +function testArraysMap(): void +{ + Arrays::map([1], fn() => throw new Exception); +} + + +/** @param array $array */ +function testArraysMapWithKeys(array $array): void +{ + Arrays::mapWithKeys($array, function (int $v): array { + if ($v < 0) { + throw new Exception; + } + return [$v, $v]; + }); +} + + +function testIterablesFirst(): void +{ + Iterables::first([1], fn() => throw new Exception); + Iterables::first([1], null, fn() => throw new Exception); +} + + +function testIterablesFirstKey(): void +{ + Iterables::firstKey([1], fn() => throw new Exception); + Iterables::firstKey([1], null, fn() => throw new Exception); +} + + +function testIterablesSome(): void +{ + Iterables::some([1], fn() => throw new Exception); +} + + +function testIterablesEvery(): void +{ + Iterables::every([1], fn() => throw new Exception); +} + + +function testStringsReplace(): void +{ + Strings::replace('a', '#a#', fn() => throw new Exception); +} + + +function testHelpersCapture(): void +{ + Helpers::capture(fn() => throw new Exception); +} + + +function testCallbackInvokeSafe(): void +{ + Callback::invokeSafe('strlen', ['a'], fn() => throw new Exception); +} + + +function testProcessWait(Process $process): void +{ + $process->wait(fn() => throw new Exception); +} + + +// lazy methods store the callback and invoke it later, so the exception is not thrown by the call +function testIterablesMapIsLazy(): void +{ + Iterables::map([1], fn() => throw new Exception); +} + + +function testFinderFilterIsLazy(): void +{ + Finder::findFiles()->filter(fn() => throw new Exception); +}