Skip to content

Commit 2da5af5

Browse files
committed
ForbidCheckedExceptionInCallableRule: replace immediatelyCalledCallables with native param-immediately-invoked-callable
1 parent 484a1c0 commit 2da5af5

15 files changed

+402
-934
lines changed

README.md

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,29 +49,6 @@ parameters:
4949
blacklist: ['(array)', '(object)', '(unset)']
5050
forbidCheckedExceptionInCallable:
5151
enabled: true
52-
immediatelyCalledCallables:
53-
array_reduce: 1
54-
array_intersect_ukey: 2
55-
array_uintersect: 2
56-
array_uintersect_assoc: 2
57-
array_intersect_uassoc: 2
58-
array_uintersect_uassoc: [2, 3]
59-
array_diff_ukey: 2
60-
array_udiff: 2
61-
array_udiff_assoc: 2
62-
array_diff_uassoc: 2
63-
array_udiff_uassoc: [2, 3]
64-
array_filter: 1
65-
array_map: 0
66-
array_walk_recursive: 1
67-
array_walk: 1
68-
call_user_func: 0
69-
call_user_func_array: 0
70-
forward_static_call: 0
71-
forward_static_call_array: 0
72-
uasort: 1
73-
uksort: 1
74-
usort: 1
7552
allowedCheckedExceptionCallables: []
7653
forbidCheckedExceptionInYieldingMethod:
7754
enabled: true
@@ -350,8 +327,7 @@ parameters:
350327

351328
### forbidCheckedExceptionInCallable
352329
- Denies throwing [checked exception](https://phpstan.org/blog/bring-your-exceptions-under-control) in callables (Closures, Arrow functions and First class callables) as those cannot be tracked as checked by PHPStan analysis, because it is unknown when the callable is about to be called
353-
- It allows configuration of functions/methods, where the callable is called immediately, those cases are allowed and are also added to [dynamic throw type extension](https://phpstan.org/developing-extensions/dynamic-throw-type-extensions) which causes those exceptions to be tracked properly in your codebase (!)
354-
- By default, native functions like `array_map` are present. So it is recommended not to overwrite the defaults here (by `!` char).
330+
- It is allowed to throw checked exceptions in immediately called callables (e.g. params marked by `@param-immediately-invoked-callable`, see [docs](https://phpstan.org/writing-php-code/phpdocs-basics#callables))
355331
- It allows configuration of functions/methods, where the callable is handling all thrown exceptions and it is safe to throw anything from there; this basically makes such calls ignored by this rule
356332
- It ignores [implicitly thrown Throwable](https://phpstan.org/blog/bring-your-exceptions-under-control#what-does-absent-%40throws-above-a-function-mean%3F)
357333
- Learn more in 🇨🇿 [talk about checked exceptions in general](https://www.youtube.com/watch?v=UQsP1U0sVZM)
@@ -360,10 +336,6 @@ parameters:
360336
parameters:
361337
shipmonkRules:
362338
forbidCheckedExceptionInCallable:
363-
immediatelyCalledCallables:
364-
'Doctrine\ORM\EntityManager::transactional': 0 # 0 is argument index where the closure appears, you can use list if needed
365-
'Symfony\Contracts\Cache\CacheInterface::get': 1
366-
'Acme\my_custom_function': 0
367339
allowedCheckedExceptionCallables:
368340
'Symfony\Component\Console\Question::setValidator': 0 # symfony automatically converts all thrown exceptions to error output, so it is safe to throw anything here
369341
```
@@ -384,16 +356,26 @@ parameters:
384356

385357

386358
```php
359+
class TransactionManager {
360+
/**
361+
* @param-immediately-invoked-callable $callback
362+
*/
363+
public function transactional(callable $callback): void {
364+
// ...
365+
$callback();
366+
// ...
367+
}
368+
}
369+
387370
class UserEditFacade
388371
{
389372
/**
390373
* @throws UserNotFoundException
391-
* ^ This throws would normally be reported as never thrown in native phpstan, but we know the closure is immediately called
392374
*/
393375
public function updateUserEmail(UserId $userId, Email $email): void
394376
{
395-
$this->entityManager->transactional(function () use ($userId, $email) {
396-
$user = $this->userRepository->get($userId); // throws checked UserNotFoundException
377+
$this->transactionManager->transactional(function () use ($userId, $email) {
378+
$user = $this->userRepository->get($userId); // can throw checked UserNotFoundException
397379
$user->updateEmail($email);
398380
})
399381
}

composer-dependency-analyser.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,4 @@
88
require_once('phar://phpstan.phar/preload.php'); // prepends PHPStan's PharAutolaoder to composer's autoloader
99

1010
return (new Configuration())
11-
->addPathToExclude(__DIR__ . '/tests/Rule/data')
12-
->addPathToExclude(__DIR__ . '/tests/Extension/data');
11+
->addPathToExclude(__DIR__ . '/tests/Rule/data');

composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@
3434
"ShipMonk\\PHPStan\\": "tests/"
3535
},
3636
"classmap": [
37-
"tests/Rule/data",
38-
"tests/Extension/data"
37+
"tests/Rule/data"
3938
]
4039
},
4140
"config": {

rules.neon

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,6 @@ parameters:
2828
blacklist: ['(array)', '(object)', '(unset)']
2929
forbidCheckedExceptionInCallable:
3030
enabled: true
31-
immediatelyCalledCallables:
32-
array_reduce: 1
33-
array_intersect_ukey: 2
34-
array_uintersect: 2
35-
array_uintersect_assoc: 2
36-
array_intersect_uassoc: 2
37-
array_uintersect_uassoc: [2, 3]
38-
array_diff_ukey: 2
39-
array_udiff: 2
40-
array_udiff_assoc: 2
41-
array_diff_uassoc: 2
42-
array_udiff_uassoc: [2, 3]
43-
array_filter: 1
44-
array_map: 0
45-
array_walk_recursive: 1
46-
array_walk: 1
47-
call_user_func: 0
48-
call_user_func_array: 0
49-
forward_static_call: 0
50-
forward_static_call_array: 0
51-
uasort: 1
52-
uksort: 1
53-
usort: 1
5431
allowedCheckedExceptionCallables: []
5532
forbidCheckedExceptionInYieldingMethod:
5633
enabled: true
@@ -147,7 +124,6 @@ parametersSchema:
147124
])
148125
forbidCheckedExceptionInCallable: structure([
149126
enabled: bool()
150-
immediatelyCalledCallables: arrayOf(anyOf(listOf(int()), int()), string())
151127
allowedCheckedExceptionCallables: arrayOf(anyOf(listOf(int()), int()), string())
152128
])
153129
forbidCheckedExceptionInYieldingMethod: structure([
@@ -302,8 +278,6 @@ conditionalTags:
302278
ShipMonk\PHPStan\Rule\UselessPrivatePropertyNullabilityRule:
303279
phpstan.rules.rule: %shipmonkRules.uselessPrivatePropertyNullability.enabled%
304280

305-
ShipMonk\PHPStan\Visitor\ImmediatelyCalledCallableVisitor:
306-
phpstan.parser.richParserNodeVisitor: %shipmonkRules.forbidCheckedExceptionInCallable.enabled%
307281
ShipMonk\PHPStan\Visitor\UnusedExceptionVisitor:
308282
phpstan.parser.richParserNodeVisitor: %shipmonkRules.forbidUnusedException.enabled%
309283
ShipMonk\PHPStan\Visitor\UnusedMatchVisitor:
@@ -313,11 +287,6 @@ conditionalTags:
313287
ShipMonk\PHPStan\Visitor\ClassPropertyAssignmentVisitor:
314288
phpstan.parser.richParserNodeVisitor: %shipmonkRules.uselessPrivatePropertyNullability.enabled%
315289

316-
ShipMonk\PHPStan\Extension\ImmediatelyCalledCallableThrowTypeExtension:
317-
phpstan.dynamicFunctionThrowTypeExtension: %shipmonkRules.forbidCheckedExceptionInCallable.enabled%
318-
phpstan.dynamicMethodThrowTypeExtension: %shipmonkRules.forbidCheckedExceptionInCallable.enabled%
319-
phpstan.dynamicStaticMethodThrowTypeExtension: %shipmonkRules.forbidCheckedExceptionInCallable.enabled%
320-
321290
services:
322291
-
323292
class: ShipMonk\PHPStan\Rule\AllowComparingOnlyComparableTypesRule
@@ -352,7 +321,6 @@ services:
352321
-
353322
class: ShipMonk\PHPStan\Rule\ForbidCheckedExceptionInCallableRule
354323
arguments:
355-
immediatelyCalledCallables: %shipmonkRules.forbidCheckedExceptionInCallable.immediatelyCalledCallables%
356324
allowedCheckedExceptionCallables: %shipmonkRules.forbidCheckedExceptionInCallable.allowedCheckedExceptionCallables%
357325
-
358326
class: ShipMonk\PHPStan\Rule\ForbidCheckedExceptionInYieldingMethodRule
@@ -422,11 +390,6 @@ services:
422390
class: ShipMonk\PHPStan\Rule\RequirePreviousExceptionPassRule
423391
arguments:
424392
reportEvenIfExceptionIsNotAcceptableByRethrownOne: %shipmonkRules.requirePreviousExceptionPass.reportEvenIfExceptionIsNotAcceptableByRethrownOne%
425-
-
426-
class: ShipMonk\PHPStan\Visitor\ImmediatelyCalledCallableVisitor
427-
arguments:
428-
immediatelyCalledCallables: %shipmonkRules.forbidCheckedExceptionInCallable.immediatelyCalledCallables%
429-
allowedCheckedExceptionCallables: %shipmonkRules.forbidCheckedExceptionInCallable.allowedCheckedExceptionCallables%
430393
-
431394
class: ShipMonk\PHPStan\Visitor\UnusedExceptionVisitor
432395
-
@@ -435,8 +398,3 @@ services:
435398
class: ShipMonk\PHPStan\Visitor\TopLevelConstructorPropertyFetchMarkingVisitor
436399
-
437400
class: ShipMonk\PHPStan\Visitor\ClassPropertyAssignmentVisitor
438-
439-
-
440-
class: ShipMonk\PHPStan\Extension\ImmediatelyCalledCallableThrowTypeExtension
441-
arguments:
442-
immediatelyCalledCallables: %shipmonkRules.forbidCheckedExceptionInCallable.immediatelyCalledCallables%

0 commit comments

Comments
 (0)