Skip to content

Commit

Permalink
Inline @throws support
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed May 3, 2021
1 parent 00d3ef4 commit 48bfa70
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 15 deletions.
52 changes: 43 additions & 9 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ private function processStmtNode(

$nodeCallback($stmt, $scope);

$overridingThrowPoints = $this->getOverridingThrowPoints($stmt, $scope);

if ($stmt instanceof Node\Stmt\Declare_) {
$hasYield = false;
$throwPoints = [];
Expand Down Expand Up @@ -554,6 +556,8 @@ private function processStmtNode(
$scope = $result->getScope();
$hasYield = $hasYield || $result->hasYield();
}

$throwPoints = $overridingThrowPoints ?? $throwPoints;
} elseif ($stmt instanceof Return_) {
if ($stmt->expr !== null) {
$result = $this->processExprNode($stmt->expr, $scope, $nodeCallback, ExpressionContext::createDeep());
Expand All @@ -567,7 +571,7 @@ private function processStmtNode(

return new StatementResult($scope, $hasYield, true, [
new StatementExitPoint($stmt, $scope),
], $throwPoints);
], $overridingThrowPoints ?? $throwPoints);
} elseif ($stmt instanceof Continue_ || $stmt instanceof Break_) {
if ($stmt->num !== null) {
$result = $this->processExprNode($stmt->num, $scope, $nodeCallback, ExpressionContext::createDeep());
Expand All @@ -581,7 +585,7 @@ private function processStmtNode(

return new StatementResult($scope, $hasYield, true, [
new StatementExitPoint($stmt, $scope),
], $throwPoints);
], $overridingThrowPoints ?? $throwPoints);
} elseif ($stmt instanceof Node\Stmt\Expression) {
$earlyTerminationExpr = $this->findEarlyTerminatingExpr($stmt->expr, $scope);
$result = $this->processExprNode($stmt->expr, $scope, $nodeCallback, ExpressionContext::createTopLevel());
Expand All @@ -596,9 +600,9 @@ private function processStmtNode(
if ($earlyTerminationExpr !== null) {
return new StatementResult($scope, $hasYield, true, [
new StatementExitPoint($stmt, $scope),
], $throwPoints);
], $overridingThrowPoints ?? $throwPoints);
}
return new StatementResult($scope, $hasYield, false, [], $throwPoints);
return new StatementResult($scope, $hasYield, false, [], $overridingThrowPoints ?? $throwPoints);
} elseif ($stmt instanceof Node\Stmt\Namespace_) {
if ($stmt->name !== null) {
$scope = $scope->enterNamespace($stmt->name->toString());
Expand Down Expand Up @@ -692,7 +696,7 @@ private function processStmtNode(
$ifAlwaysTrue = $conditionType instanceof ConstantBooleanType && $conditionType->getValue();
$condResult = $this->processExprNode($stmt->cond, $scope, $nodeCallback, ExpressionContext::createDeep());
$exitPoints = [];
$throwPoints = $condResult->getThrowPoints();
$throwPoints = $overridingThrowPoints ?? $condResult->getThrowPoints();
$finalScope = null;
$alwaysTerminating = true;
$hasYield = $condResult->hasYield();
Expand Down Expand Up @@ -779,7 +783,7 @@ private function processStmtNode(
$this->processTraitUse($stmt, $scope, $nodeCallback);
} elseif ($stmt instanceof Foreach_) {
$condResult = $this->processExprNode($stmt->expr, $scope, $nodeCallback, ExpressionContext::createDeep());
$throwPoints = $condResult->getThrowPoints();
$throwPoints = $overridingThrowPoints ?? $condResult->getThrowPoints();
$scope = $condResult->getScope();
$arrayComparisonExpr = new BinaryOp\NotIdentical(
$stmt->expr,
Expand Down Expand Up @@ -905,7 +909,7 @@ private function processStmtNode(
$finalScope = $finalScope->mergeWith($condScope);
}

$throwPoints = $condResult->getThrowPoints();
$throwPoints = $overridingThrowPoints ?? $condResult->getThrowPoints();
if (!$neverIterates) {
$throwPoints = array_merge($throwPoints, $finalScopeResult->getThrowPoints());
}
Expand Down Expand Up @@ -1392,15 +1396,45 @@ private function processStmtNode(
} elseif ($stmt instanceof Node\Stmt\Nop) {
$scope = $this->processStmtVarAnnotation($scope, $stmt, null);
$hasYield = false;
$throwPoints = [];
$throwPoints = $overridingThrowPoints ?? [];
} else {
$hasYield = false;
$throwPoints = [];
$throwPoints = $overridingThrowPoints ?? [];
}

return new StatementResult($scope, $hasYield, false, [], $throwPoints);
}

/**
* @param Node\Stmt $statement
* @param MutatingScope $scope
* @return ThrowPoint[]|null
*/
private function getOverridingThrowPoints(Node\Stmt $statement, MutatingScope $scope): ?array
{
foreach ($statement->getComments() as $comment) {
if (!$comment instanceof Doc) {
continue;
}

$function = $scope->getFunction();
$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
$scope->getFile(),
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
$function !== null ? $function->getName() : null,
$comment->getText()
);

$throwsTag = $resolvedPhpDoc->getThrowsTag();
if ($throwsTag !== null) {
return [ThrowPoint::createExplicit($scope, $throwsTag->getType(), $statement, false)];
}
}

return null;
}

private function getCurrentClassReflection(Node\Stmt\ClassLike $stmt, Scope $scope): ClassReflection
{
$className = $stmt->namespacedName->toString();
Expand Down
14 changes: 8 additions & 6 deletions src/Rules/PhpDoc/InvalidThrowsPhpDocValueRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use PHPStan\Type\VoidType;

/**
* @implements \PHPStan\Rules\Rule<\PhpParser\Node\FunctionLike>
* @implements \PHPStan\Rules\Rule<\PhpParser\Node\Stmt>
*/
class InvalidThrowsPhpDocValueRule implements \PHPStan\Rules\Rule
{
Expand All @@ -25,7 +25,7 @@ public function __construct(FileTypeMapper $fileTypeMapper)

public function getNodeType(): string
{
return \PhpParser\Node\FunctionLike::class;
return \PhpParser\Node\Stmt::class;
}

public function processNode(Node $node, Scope $scope): array
Expand All @@ -35,11 +35,13 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

if ($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassMethod) {
return []; // is handled by virtual nodes
}

$functionName = null;
if ($node instanceof Node\Stmt\ClassMethod) {
$functionName = $node->name->name;
} elseif ($node instanceof Node\Stmt\Function_) {
$functionName = trim($scope->getNamespace() . '\\' . $node->name->name, '\\');
if ($scope->getFunction() !== null) {
$functionName = $scope->getFunction()->getName();
}

$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
Expand Down
25 changes: 25 additions & 0 deletions tests/PHPStan/Rules/Exceptions/data/unthrown-exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,28 @@ public function doConsecteur()
}

}

class InlineThrows
{

public function doFoo()
{
try {
/** @throws \InvalidArgumentException */
echo 1;
} catch (\InvalidArgumentException $e) {

}
}

public function doBar()
{
try {
/** @throws \InvalidArgumentException */
$i = 1;
} catch (\InvalidArgumentException $e) {

}
}

}
4 changes: 4 additions & 0 deletions tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ public function testRule(): void
'PHPDoc tag @var has invalid value ((Foo|Bar): Unexpected token "*/", expected \')\' at offset 18',
62,
],
[
'PHPDoc tag @throws has invalid value ((\Exception): Unexpected token "*/", expected \')\' at offset 24',
72,
],
]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ public function testRule(): void
'PHPDoc tag @throws with type stdClass|void is not subtype of Throwable',
103,
],
[
'PHPDoc tag @throws with type stdClass is not subtype of Throwable',
118,
],
]);
}

Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Rules/PhpDoc/data/incompatible-throws.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,9 @@ function voidUnionWithNotThrowableThrows()
function exceptionTemplateThrows()
{
}

function inlineThrows()
{
/** @throws \stdClass */
$i = 1;
}
11 changes: 11 additions & 0 deletions tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,14 @@ class Baz
private $barProperty;

}

class InlineThrows
{

public function doFoo()
{
/** @throws (\Exception */
$i = 1;
}

}

0 comments on commit 48bfa70

Please sign in to comment.