Skip to content

Commit

Permalink
ExceptionTypeResolver - support listing checked exception types
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed May 14, 2021
1 parent 0a87e2f commit 349b3b3
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 4 deletions.
6 changes: 6 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ parameters:
exceptions:
uncheckedExceptionRegexes: []
uncheckedExceptionClasses: []
checkedExceptionRegexes: []
checkedExceptionClasses: []
check:
missingCheckedExceptionInThrows: false
tooWideThrowType: false
Expand Down Expand Up @@ -179,6 +181,8 @@ parametersSchema:
exceptions: structure([
uncheckedExceptionRegexes: listOf(string()),
uncheckedExceptionClasses: listOf(string()),
checkedExceptionRegexes: listOf(string()),
checkedExceptionClasses: listOf(string()),
check: structure([
missingCheckedExceptionInThrows: bool(),
tooWideThrowType: bool()
Expand Down Expand Up @@ -748,6 +752,8 @@ services:
arguments:
uncheckedExceptionRegexes: %exceptions.uncheckedExceptionRegexes%
uncheckedExceptionClasses: %exceptions.uncheckedExceptionClasses%
checkedExceptionRegexes: %exceptions.checkedExceptionRegexes%
checkedExceptionClasses: %exceptions.checkedExceptionClasses%

-
class: PHPStan\Rules\Exceptions\MissingCheckedExceptionInFunctionThrowsRule
Expand Down
52 changes: 49 additions & 3 deletions src/Rules/Exceptions/ExceptionTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,32 @@ class ExceptionTypeResolver
/** @var string[] */
private array $uncheckedExceptionClasses;

/** @var string[] */
private array $checkedExceptionRegexes;

/** @var string[] */
private array $checkedExceptionClasses;

/**
* @param ReflectionProvider $reflectionProvider
* @param string[] $uncheckedExceptionRegexes
* @param string[] $uncheckedExceptionClasses
* @param string[] $checkedExceptionRegexes
* @param string[] $checkedExceptionClasses
*/
public function __construct(
ReflectionProvider $reflectionProvider,
array $uncheckedExceptionRegexes,
array $uncheckedExceptionClasses
array $uncheckedExceptionClasses,
array $checkedExceptionRegexes,
array $checkedExceptionClasses
)
{
$this->reflectionProvider = $reflectionProvider;
$this->uncheckedExceptionRegexes = $uncheckedExceptionRegexes;
$this->uncheckedExceptionClasses = $uncheckedExceptionClasses;
$this->checkedExceptionRegexes = $checkedExceptionRegexes;
$this->checkedExceptionClasses = $checkedExceptionClasses;
}

public function isCheckedException(string $className): bool
Expand All @@ -47,7 +59,7 @@ public function isCheckedException(string $className): bool
}

if (!$this->reflectionProvider->hasClass($className)) {
return true;
return $this->isCheckedExceptionInternal($className);
}

$classReflection = $this->reflectionProvider->getClass($className);
Expand All @@ -63,7 +75,41 @@ public function isCheckedException(string $className): bool
return false;
}

return true;
return $this->isCheckedExceptionInternal($className);
}

private function isCheckedExceptionInternal(string $className): bool
{
foreach ($this->checkedExceptionRegexes as $regex) {
if (Strings::match($className, $regex) !== null) {
return true;
}
}

foreach ($this->checkedExceptionClasses as $checkedExceptionClass) {
if ($className === $checkedExceptionClass) {
return true;
}
}

if (!$this->reflectionProvider->hasClass($className)) {
return count($this->checkedExceptionRegexes) === 0 && count($this->checkedExceptionClasses) === 0;
}

$classReflection = $this->reflectionProvider->getClass($className);
foreach ($this->checkedExceptionClasses as $checkedExceptionClass) {
if ($classReflection->getName() === $checkedExceptionClass) {
return true;
}

if (!$classReflection->isSubclassOf($checkedExceptionClass)) {
continue;
}

return true;
}

return count($this->checkedExceptionRegexes) === 0 && count($this->checkedExceptionClasses) === 0;
}

}
68 changes: 67 additions & 1 deletion tests/PHPStan/Rules/Exceptions/ExceptionTypeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public function dataIsCheckedException(): array
{
return [
[
[],
[],
[],
[],
\InvalidArgumentException::class,
Expand All @@ -21,6 +23,8 @@ public function dataIsCheckedException(): array
'#^InvalidArgumentException$#',
],
[],
[],
[],
\InvalidArgumentException::class,
false,
],
Expand All @@ -29,6 +33,8 @@ public function dataIsCheckedException(): array
[
\InvalidArgumentException::class,
],
[],
[],
\InvalidArgumentException::class,
false,
],
Expand All @@ -37,6 +43,8 @@ public function dataIsCheckedException(): array
[
\LogicException::class,
],
[],
[],
\LogicException::class,
false,
],
Expand All @@ -45,6 +53,8 @@ public function dataIsCheckedException(): array
[
\LogicException::class,
],
[],
[],
\DomainException::class,
false,
],
Expand All @@ -53,27 +63,83 @@ public function dataIsCheckedException(): array
[
\DomainException::class,
],
[],
[],
\LogicException::class,
true,
],
[
[],
[],
[
'#^Exception$#',
],
[],
\InvalidArgumentException::class,
false,
],
[
[],
[],
[
'#^InvalidArgumentException#',
],
[],
\InvalidArgumentException::class,
true,
],
[
[],
[],
[],
[
\DomainException::class,
],
\InvalidArgumentException::class,
false,
],
[
[],
[],
[],
[
\InvalidArgumentException::class,
],
\InvalidArgumentException::class,
true,
],
[
[],
[],
[],
[
\LogicException::class,
],
\InvalidArgumentException::class,
true,
],
];
}

/**
* @dataProvider dataIsCheckedException
* @param string[] $uncheckedExceptionRegexes
* @param string[] $uncheckedExceptionClasses
* @param string[] $checkedExceptionRegexes
* @param string[] $checkedExceptionClasses
* @param string $className
* @param bool $expectedResult
*/
public function testIsCheckedException(
array $uncheckedExceptionRegexes,
array $uncheckedExceptionClasses,
array $checkedExceptionRegexes,
array $checkedExceptionClasses,
string $className,
bool $expectedResult
): void
{
$resolver = new ExceptionTypeResolver($this->createBroker(), $uncheckedExceptionRegexes, $uncheckedExceptionClasses);
$resolver = new ExceptionTypeResolver($this->createBroker(), $uncheckedExceptionRegexes, $uncheckedExceptionClasses, $checkedExceptionRegexes, $checkedExceptionClasses);
$this->assertSame($expectedResult, $resolver->isCheckedException($className));
}

Expand Down

0 comments on commit 349b3b3

Please sign in to comment.