Skip to content

forbidCheckedExceptionInCallable: replace immediatelyCalledCallables with native @param-immediately-invoked-callable #240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 14 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,6 @@ parameters:
blacklist: ['(array)', '(object)', '(unset)']
forbidCheckedExceptionInCallable:
enabled: true
immediatelyCalledCallables:
array_reduce: 1
array_intersect_ukey: 2
array_uintersect: 2
array_uintersect_assoc: 2
array_intersect_uassoc: 2
array_uintersect_uassoc: [2, 3]
array_diff_ukey: 2
array_udiff: 2
array_udiff_assoc: 2
array_diff_uassoc: 2
array_udiff_uassoc: [2, 3]
array_filter: 1
array_map: 0
array_walk_recursive: 1
array_walk: 1
call_user_func: 0
call_user_func_array: 0
forward_static_call: 0
forward_static_call_array: 0
uasort: 1
uksort: 1
usort: 1
allowedCheckedExceptionCallables: []
forbidCheckedExceptionInYieldingMethod:
enabled: true
Expand Down Expand Up @@ -350,8 +327,7 @@ parameters:

### forbidCheckedExceptionInCallable
- 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
- 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 (!)
- By default, native functions like `array_map` are present. So it is recommended not to overwrite the defaults here (by `!` char).
- 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))
- 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
- It ignores [implicitly thrown Throwable](https://phpstan.org/blog/bring-your-exceptions-under-control#what-does-absent-%40throws-above-a-function-mean%3F)
- Learn more in 🇨🇿 [talk about checked exceptions in general](https://www.youtube.com/watch?v=UQsP1U0sVZM)
Expand All @@ -360,10 +336,6 @@ parameters:
parameters:
shipmonkRules:
forbidCheckedExceptionInCallable:
immediatelyCalledCallables:
'Doctrine\ORM\EntityManager::transactional': 0 # 0 is argument index where the closure appears, you can use list if needed
'Symfony\Contracts\Cache\CacheInterface::get': 1
'Acme\my_custom_function': 0
allowedCheckedExceptionCallables:
'Symfony\Component\Console\Question::setValidator': 0 # symfony automatically converts all thrown exceptions to error output, so it is safe to throw anything here
```
Expand All @@ -384,16 +356,26 @@ parameters:


```php
class TransactionManager {
/**
* @param-immediately-invoked-callable $callback
*/
public function transactional(callable $callback): void {
// ...
$callback();
// ...
}
}

class UserEditFacade
{
/**
* @throws UserNotFoundException
* ^ This throws would normally be reported as never thrown in native phpstan, but we know the closure is immediately called
*/
public function updateUserEmail(UserId $userId, Email $email): void
{
$this->entityManager->transactional(function () use ($userId, $email) {
$user = $this->userRepository->get($userId); // throws checked UserNotFoundException
$this->transactionManager->transactional(function () use ($userId, $email) {
$user = $this->userRepository->get($userId); // can throw checked UserNotFoundException
$user->updateEmail($email);
})
}
Expand Down
3 changes: 1 addition & 2 deletions composer-dependency-analyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@
require_once('phar://phpstan.phar/preload.php'); // prepends PHPStan's PharAutolaoder to composer's autoloader

return (new Configuration())
->addPathToExclude(__DIR__ . '/tests/Rule/data')
->addPathToExclude(__DIR__ . '/tests/Extension/data');
->addPathToExclude(__DIR__ . '/tests/Rule/data');
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@
"ShipMonk\\PHPStan\\": "tests/"
},
"classmap": [
"tests/Rule/data",
"tests/Extension/data"
"tests/Rule/data"
]
},
"config": {
Expand Down
42 changes: 0 additions & 42 deletions rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,6 @@ parameters:
blacklist: ['(array)', '(object)', '(unset)']
forbidCheckedExceptionInCallable:
enabled: true
immediatelyCalledCallables:
array_reduce: 1
array_intersect_ukey: 2
array_uintersect: 2
array_uintersect_assoc: 2
array_intersect_uassoc: 2
array_uintersect_uassoc: [2, 3]
array_diff_ukey: 2
array_udiff: 2
array_udiff_assoc: 2
array_diff_uassoc: 2
array_udiff_uassoc: [2, 3]
array_filter: 1
array_map: 0
array_walk_recursive: 1
array_walk: 1
call_user_func: 0
call_user_func_array: 0
forward_static_call: 0
forward_static_call_array: 0
uasort: 1
uksort: 1
usort: 1
allowedCheckedExceptionCallables: []
forbidCheckedExceptionInYieldingMethod:
enabled: true
Expand Down Expand Up @@ -147,7 +124,6 @@ parametersSchema:
])
forbidCheckedExceptionInCallable: structure([
enabled: bool()
immediatelyCalledCallables: arrayOf(anyOf(listOf(int()), int()), string())
allowedCheckedExceptionCallables: arrayOf(anyOf(listOf(int()), int()), string())
])
forbidCheckedExceptionInYieldingMethod: structure([
Expand Down Expand Up @@ -302,8 +278,6 @@ conditionalTags:
ShipMonk\PHPStan\Rule\UselessPrivatePropertyNullabilityRule:
phpstan.rules.rule: %shipmonkRules.uselessPrivatePropertyNullability.enabled%

ShipMonk\PHPStan\Visitor\ImmediatelyCalledCallableVisitor:
phpstan.parser.richParserNodeVisitor: %shipmonkRules.forbidCheckedExceptionInCallable.enabled%
ShipMonk\PHPStan\Visitor\UnusedExceptionVisitor:
phpstan.parser.richParserNodeVisitor: %shipmonkRules.forbidUnusedException.enabled%
ShipMonk\PHPStan\Visitor\UnusedMatchVisitor:
Expand All @@ -313,11 +287,6 @@ conditionalTags:
ShipMonk\PHPStan\Visitor\ClassPropertyAssignmentVisitor:
phpstan.parser.richParserNodeVisitor: %shipmonkRules.uselessPrivatePropertyNullability.enabled%

ShipMonk\PHPStan\Extension\ImmediatelyCalledCallableThrowTypeExtension:
phpstan.dynamicFunctionThrowTypeExtension: %shipmonkRules.forbidCheckedExceptionInCallable.enabled%
phpstan.dynamicMethodThrowTypeExtension: %shipmonkRules.forbidCheckedExceptionInCallable.enabled%
phpstan.dynamicStaticMethodThrowTypeExtension: %shipmonkRules.forbidCheckedExceptionInCallable.enabled%

services:
-
class: ShipMonk\PHPStan\Rule\AllowComparingOnlyComparableTypesRule
Expand Down Expand Up @@ -352,7 +321,6 @@ services:
-
class: ShipMonk\PHPStan\Rule\ForbidCheckedExceptionInCallableRule
arguments:
immediatelyCalledCallables: %shipmonkRules.forbidCheckedExceptionInCallable.immediatelyCalledCallables%
allowedCheckedExceptionCallables: %shipmonkRules.forbidCheckedExceptionInCallable.allowedCheckedExceptionCallables%
-
class: ShipMonk\PHPStan\Rule\ForbidCheckedExceptionInYieldingMethodRule
Expand Down Expand Up @@ -422,11 +390,6 @@ services:
class: ShipMonk\PHPStan\Rule\RequirePreviousExceptionPassRule
arguments:
reportEvenIfExceptionIsNotAcceptableByRethrownOne: %shipmonkRules.requirePreviousExceptionPass.reportEvenIfExceptionIsNotAcceptableByRethrownOne%
-
class: ShipMonk\PHPStan\Visitor\ImmediatelyCalledCallableVisitor
arguments:
immediatelyCalledCallables: %shipmonkRules.forbidCheckedExceptionInCallable.immediatelyCalledCallables%
allowedCheckedExceptionCallables: %shipmonkRules.forbidCheckedExceptionInCallable.allowedCheckedExceptionCallables%
-
class: ShipMonk\PHPStan\Visitor\UnusedExceptionVisitor
-
Expand All @@ -435,8 +398,3 @@ services:
class: ShipMonk\PHPStan\Visitor\TopLevelConstructorPropertyFetchMarkingVisitor
-
class: ShipMonk\PHPStan\Visitor\ClassPropertyAssignmentVisitor

-
class: ShipMonk\PHPStan\Extension\ImmediatelyCalledCallableThrowTypeExtension
arguments:
immediatelyCalledCallables: %shipmonkRules.forbidCheckedExceptionInCallable.immediatelyCalledCallables%
Loading