Skip to content

Commit

Permalink
MissingCheckedExceptionInThrowsCheck - report new catch position
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed May 5, 2021
1 parent 179b213 commit 810e504
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function processNode(Node $node, Scope $scope): array
}

$errors = [];
foreach ($this->check->check($functionReflection->getThrowType(), $statementResult->getThrowPoints()) as [$className, $throwPointNode]) {
foreach ($this->check->check($functionReflection->getThrowType(), $statementResult->getThrowPoints()) as [$className, $throwPointNode, $newCatchPosition]) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Function %s() throws checked exception %s but it\'s missing from the PHPDoc @throws tag.',
$functionReflection->getName(),
Expand All @@ -46,6 +46,7 @@ public function processNode(Node $node, Scope $scope): array
->identifier('exceptions.missingThrowsTag')
->metadata([
'exceptionName' => $className,
'newCatchPosition' => $newCatchPosition,
'statementDepth' => $throwPointNode->getAttribute('statementDepth'),
'statementOrder' => $throwPointNode->getAttribute('statementOrder'),
])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function processNode(Node $node, Scope $scope): array
}

$errors = [];
foreach ($this->check->check($methodReflection->getThrowType(), $statementResult->getThrowPoints()) as [$className, $throwPointNode]) {
foreach ($this->check->check($methodReflection->getThrowType(), $statementResult->getThrowPoints()) as [$className, $throwPointNode, $newCatchPosition]) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Method %s::%s() throws checked exception %s but it\'s missing from the PHPDoc @throws tag.',
$methodReflection->getDeclaringClass()->getDisplayName(),
Expand All @@ -47,6 +47,7 @@ public function processNode(Node $node, Scope $scope): array
->identifier('exceptions.missingThrowsTag')
->metadata([
'exceptionName' => $className,
'newCatchPosition' => $newCatchPosition,
'statementDepth' => $throwPointNode->getAttribute('statementDepth'),
'statementOrder' => $throwPointNode->getAttribute('statementOrder'),
])
Expand Down
49 changes: 47 additions & 2 deletions src/Rules/Exceptions/MissingCheckedExceptionInThrowsCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\Type\NeverType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\VerbosityLevel;
Expand All @@ -24,7 +25,7 @@ public function __construct(ExceptionTypeResolver $exceptionTypeResolver)
/**
* @param Type|null $throwType
* @param ThrowPoint[] $throwPoints
* @return array<int, array{string, Node\Expr|Node\Stmt}>
* @return array<int, array{string, Node\Expr|Node\Stmt, int|null}>
*/
public function check(?Type $throwType, array $throwPoints): array
{
Expand Down Expand Up @@ -53,11 +54,55 @@ public function check(?Type $throwType, array $throwPoints): array
continue;
}

$classes[] = [$throwPointType->describe(VerbosityLevel::typeOnly()), $throwPoint->getNode()];
$classes[] = [$throwPointType->describe(VerbosityLevel::typeOnly()), $throwPoint->getNode(), $this->getNewCatchPosition($throwPointType, $throwPoint->getNode())];
}
}

return $classes;
}

private function getNewCatchPosition(Type $throwPointType, Node $throwPointNode): ?int
{
if ($throwPointType instanceof TypeWithClassName) {
// to get rid of type subtraction
$throwPointType = new ObjectType($throwPointType->getClassName());
}
$tryCatch = $this->findTryCatch($throwPointNode);
if ($tryCatch === null) {
return null;
}

$position = 0;
foreach ($tryCatch->catches as $catch) {
$type = TypeCombinator::union(...array_map(static function (Node\Name $class): ObjectType {
return new ObjectType($class->toString());
}, $catch->types));
if (!$throwPointType->isSuperTypeOf($type)->yes()) {
continue;
}

$position++;
}

return $position;
}

private function findTryCatch(Node $node): ?Node\Stmt\TryCatch
{
if ($node instanceof Node\FunctionLike) {
return null;
}

if ($node instanceof Node\Stmt\TryCatch) {
return $node;
}

$parent = $node->getAttribute('parent');
if ($parent === null) {
return null;
}

return $this->findTryCatch($parent);
}

}

0 comments on commit 810e504

Please sign in to comment.