Skip to content

Commit

Permalink
Understand that methods called from constructor throwing exception ca…
Browse files Browse the repository at this point in the history
…nnot leave object in an uninitializad state
  • Loading branch information
ondrejmirtes committed Jul 5, 2023
1 parent 58a4ae9 commit f4d060b
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
18 changes: 18 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2185,13 +2185,31 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$calledMethodEndScope = null;
foreach ($executionEnds as $executionEnd) {
$statementResult = $executionEnd->getStatementResult();
$endNode = $executionEnd->getNode();
if ($endNode instanceof Node\Stmt\Throw_) {
continue;
}
if ($endNode instanceof Node\Stmt\Expression) {
$exprType = $statementResult->getScope()->getType($endNode->expr);
if ($exprType instanceof NeverType && $exprType->isExplicit()) {
continue;
}
}
if ($calledMethodEndScope === null) {
$calledMethodEndScope = $statementResult->getScope();
continue;
}

$calledMethodEndScope = $calledMethodEndScope->mergeWith($statementResult->getScope());
}
foreach ($methodReturnStatementsNode->getReturnStatements() as $returnStatement) {
if ($calledMethodEndScope === null) {
$calledMethodEndScope = $returnStatement->getScope();
continue;
}

$calledMethodEndScope = $calledMethodEndScope->mergeWith($returnStatement->getScope());
}

if ($calledMethodEndScope !== null) {
$scope = $scope->mergeInitializedProperties($calledMethodEndScope);
Expand Down
64 changes: 64 additions & 0 deletions tests/PHPStan/Rules/Properties/data/uninitialized-property.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,67 @@ private function returnNever()
}

}

class InitializedInPrivateSetterWithThrow
{

private int $foo;

public function __construct()
{
$this->setFoo();
$this->doSomething();
}

private function setFoo()
{
if (rand(0, 1)) {
$this->foo = 1;
return;
}

throw new \Exception();
}

public function doSomething()
{
echo $this->foo;
}

}

class InitializedInPrivateSetterWithReturnNever
{

private int $foo;

public function __construct()
{
$this->setFoo();
$this->doSomething();
}

private function setFoo()
{
if (rand(0, 1)) {
$this->foo = 1;
return;
}

$this->returnNever();
}

public function doSomething()
{
echo $this->foo;
}

/**
* @return never
*/
private function returnNever()
{
throw new \Exception();
}

}

0 comments on commit f4d060b

Please sign in to comment.