Skip to content

Commit

Permalink
Initial commit for an @throws check. Small modifications for retrievi…
Browse files Browse the repository at this point in the history
…ng a list of fully exception types (with their namespaces expanded) thrown by a function or method from the Symbol table.
  • Loading branch information
jongardiner committed Feb 23, 2024
1 parent caccd65 commit 1f4b30f
Showing 1 changed file with 83 additions and 0 deletions.
83 changes: 83 additions & 0 deletions src/Checks/ThrowsCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace BambooHR\Guardrail\Checks;

use BambooHR\Guardrail\Checks\BaseCheck;
use BambooHR\Guardrail\NodeVisitors\ForEachNode;
use BambooHR\Guardrail\Scope;
use BambooHR\Guardrail\TypeComparer;
use BambooHR\Guardrail\Util;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;

class ThrowsCheck extends BaseCheck {

function getCheckNodeTypes() {
return [Node\Expr\Throw_::class, Node\Stmt\Throw_::class,
Node\Expr\MethodCall::class,
Node\Expr\FuncCall::class,
Node\Expr\StaticCall::class
];
}

public function run($fileName, Node $node, ClassLike $inside = null, Scope $scope = null) {

if ($node instanceof Node\Expr\Throw_ || $node instanceof Node\Stmt\Throw_) {
$throws = $node->expr->getAttribute(TypeComparer::INFERRED_TYPE_ATTR);
if ($throws instanceof Node\Name) {
if (!$this->parentCatches($scope->getParentNodes(), $throws) &&
!$this->isDocumentedThrow($scope->getInsideFunction(), $throws)
) {
$this->emitError($fileName, $node, ErrorConstants::TYPE_UNDOCUMENTED_EXCEPTION,"Undocumented exception ($throws) thrown");
}
}
} else if ($node instanceof Node\Expr\MethodCall && $node->name instanceof Node\Identifier) {
$type=$node->getAttribute(TypeComparer::INFERRED_TYPE_ATTR);
if ($type) {
TypeComparer::forEachType($type, function($typeNode) use ($node,$scope, $fileName) {
$method = Util::findAbstractedMethod($typeNode, $node->name, $this->symbolTable );
if ($method) {
$throws=$method->getThrowsList();
foreach($throws as $throw) {
if (!$this->parentCatches($scope->getParentNodes(), $throw) &&
!$this->isDocumentedThrow($scope->getInsideFunction(), $throw))
{
$this->emitError($fileName, $node, ErrorConstants::TYPE_UNDOCUMENTED_EXCEPTION,"Undocumented exception ($throw) thrown by ".$typeNode."::".$node->name);
}
}

}
});
}
}
}

function isDocumentedThrow(?Node $node, string $throw) {
if ($node) {
$documentedThrows = $node->getAttribute('throws', []);
foreach ($documentedThrows as $documentedThrow) {
if (strcasecmp($documentedThrow, $throw) == 0) {
return true;
}
}
}
return false;
}

function parentCatches($parents, $throws):bool {
foreach (array_reverse($parents) as $parent) {
if ($parent instanceof Node\Stmt\TryCatch) {
foreach ($parent->catches as $catch) {
foreach ($catch->types as $type) {
if ($this->symbolTable->isParentClassOrInterface($type,$throws)) {
return true;
}
}
}
} else if($parent instanceof Node\FunctionLike) {
return false;
}
}
return false;
}
}

0 comments on commit 1f4b30f

Please sign in to comment.