Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 10 additions & 16 deletions src/Analyser/ExpressionResultStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,38 @@

use Fiber;
use PhpParser\Node\Expr;
use PHPStan\Analyser\Fiber\ExpressionAnalysisRequest;
use PHPStan\ShouldNotHappenException;
use PHPStan\Analyser\Fiber\BeforeScopeForExprRequest;
use SplObjectStorage;
use function get_class;
use function sprintf;

final class ExpressionResultStorage
{

/** @var SplObjectStorage<Expr, ExpressionResult> */
private SplObjectStorage $results;
/** @var SplObjectStorage<Expr, Scope> */
private SplObjectStorage $scopes;

/** @var array<array{fiber: Fiber<mixed, ExpressionResult, null, ExpressionAnalysisRequest>, request: ExpressionAnalysisRequest}> */
/** @var array<array{fiber: Fiber<mixed, Scope, null, BeforeScopeForExprRequest>, request: BeforeScopeForExprRequest}> */
public array $pendingFibers = [];

public function __construct()
{
$this->results = new SplObjectStorage();
$this->scopes = new SplObjectStorage();
}

public function duplicate(): self
{
$new = new self();
$new->results->addAll($this->results);
$new->scopes->addAll($this->scopes);
return $new;
}

public function storeResult(Expr $expr, ExpressionResult $result): void
public function storeBeforeScope(Expr $expr, Scope $scope): void
{
if (isset($this->results[$expr])) {
throw new ShouldNotHappenException(sprintf('already stored %s on line %d', get_class($expr), $expr->getStartLine()));
}
$this->results[$expr] = $result;
$this->scopes[$expr] = $scope;
}

public function findResult(Expr $expr): ?ExpressionResult
public function findBeforeScope(Expr $expr): ?Scope
{
return $this->results[$expr] ?? null;
return $this->scopes[$expr] ?? null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use PhpParser\Node\Expr;
use PHPStan\Analyser\MutatingScope;

final class ExpressionAnalysisRequest
final class BeforeScopeForExprRequest
{

public function __construct(public readonly Expr $expr, public readonly MutatingScope $scope)
Expand Down
23 changes: 11 additions & 12 deletions src/Analyser/Fiber/FiberNodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use PhpParser\Node;
use PhpParser\Node\Expr;
use PHPStan\Analyser\ExpressionContext;
use PHPStan\Analyser\ExpressionResult;
use PHPStan\Analyser\ExpressionResultStorage;
use PHPStan\Analyser\MutatingScope;
use PHPStan\Analyser\NodeScopeResolver;
Expand Down Expand Up @@ -40,19 +39,19 @@ protected function callNodeCallback(
}

/**
* @param Fiber<mixed, ExpressionResult, null, ExpressionAnalysisRequest> $fiber
* @param Fiber<mixed, Scope, null, BeforeScopeForExprRequest> $fiber
*/
private function runFiberForNodeCallback(
ExpressionResultStorage $storage,
Fiber $fiber,
?ExpressionAnalysisRequest $request,
?BeforeScopeForExprRequest $request,
): void
{
while (!$fiber->isTerminated()) {
if ($request instanceof ExpressionAnalysisRequest) {
$result = $storage->findResult($request->expr);
if ($result !== null) {
$request = $fiber->resume($result);
if ($request instanceof BeforeScopeForExprRequest) {
$beforeScope = $storage->findBeforeScope($request->expr);
if ($beforeScope !== null) {
$request = $fiber->resume($beforeScope);
continue;
}

Expand All @@ -79,9 +78,9 @@ protected function processPendingFibers(ExpressionResultStorage $storage): void
{
foreach ($storage->pendingFibers as $pending) {
$request = $pending['request'];
$result = $storage->findResult($request->expr);
$beforeScope = $storage->findBeforeScope($request->expr);

if ($result !== null) {
if ($beforeScope !== null) {
throw new ShouldNotHappenException('Pending fibers at the end should be about synthetic nodes');
}

Expand All @@ -93,8 +92,8 @@ protected function processPendingFibers(ExpressionResultStorage $storage): void
new NoopNodeCallback(),
ExpressionContext::createTopLevel(),
);
if ($storage->findResult($request->expr) === null) {
throw new ShouldNotHappenException(sprintf('processExprNode should have stored the result of %s on line %s', get_class($request->expr), $request->expr->getStartLine()));
if ($storage->findBeforeScope($request->expr) === null) {
throw new ShouldNotHappenException(sprintf('processExprNode should have stored the beforeScope of %s on line %s', get_class($request->expr), $request->expr->getStartLine()));
}
$this->processPendingFibers($storage);

Expand All @@ -103,7 +102,7 @@ protected function processPendingFibers(ExpressionResultStorage $storage): void
}
}

protected function processPendingFibersForRequestedExpr(ExpressionResultStorage $storage, Expr $expr, ExpressionResult $result): void
protected function processPendingFibersForRequestedExpr(ExpressionResultStorage $storage, Expr $expr, Scope $result): void
{
$restartLoop = true;

Expand Down
26 changes: 13 additions & 13 deletions src/Analyser/Fiber/FiberScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

use Fiber;
use PhpParser\Node\Expr;
use PHPStan\Analyser\ExpressionResult;
use PHPStan\Analyser\MutatingScope;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Type;

final class FiberScope extends MutatingScope
Expand Down Expand Up @@ -41,12 +41,12 @@ public function toMutatingScope(): MutatingScope
/** @api */
public function getType(Expr $node): Type
{
/** @var ExpressionResult $exprResult */
$exprResult = Fiber::suspend(
new ExpressionAnalysisRequest($node, $this),
/** @var Scope $beforeScope */
$beforeScope = Fiber::suspend(
new BeforeScopeForExprRequest($node, $this),
);

return $exprResult->getBeforeScope()->toMutatingScope()->getType($node);
return $beforeScope->toMutatingScope()->getType($node);
}

public function getScopeType(Expr $expr): Type
Expand All @@ -62,22 +62,22 @@ public function getScopeNativeType(Expr $expr): Type
/** @api */
public function getNativeType(Expr $expr): Type
{
/** @var ExpressionResult $exprResult */
$exprResult = Fiber::suspend(
new ExpressionAnalysisRequest($expr, $this),
/** @var Scope $beforeScope */
$beforeScope = Fiber::suspend(
new BeforeScopeForExprRequest($expr, $this),
);

return $exprResult->getBeforeScope()->toMutatingScope()->getNativeType($expr);
return $beforeScope->toMutatingScope()->getNativeType($expr);
}

public function getKeepVoidType(Expr $node): Type
{
/** @var ExpressionResult $exprResult */
$exprResult = Fiber::suspend(
new ExpressionAnalysisRequest($node, $this),
/** @var Scope $beforeScope */
$beforeScope = Fiber::suspend(
new BeforeScopeForExprRequest($node, $this),
);

return $exprResult->getBeforeScope()->toMutatingScope()->getKeepVoidType($node);
return $beforeScope->toMutatingScope()->getKeepVoidType($node);
}

}
45 changes: 22 additions & 23 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -342,17 +342,17 @@ public function processNodes(
$this->processPendingFibers($expressionResultStorage);
}

private function storeResult(ExpressionResultStorage $storage, Expr $expr, ExpressionResult $result): void
private function storeBeforeScope(ExpressionResultStorage $storage, Expr $expr, ExpressionResult $result): void
{
$storage->storeResult($expr, $result);
$this->processPendingFibersForRequestedExpr($storage, $expr, $result);
$storage->storeBeforeScope($expr, $result->getBeforeScope());
$this->processPendingFibersForRequestedExpr($storage, $expr, $result->getBeforeScope());
}

protected function processPendingFibers(ExpressionResultStorage $storage): void
{
}

protected function processPendingFibersForRequestedExpr(ExpressionResultStorage $storage, Expr $expr, ExpressionResult $result): void
protected function processPendingFibersForRequestedExpr(ExpressionResultStorage $storage, Expr $expr, Scope $result): void
{
}

Expand Down Expand Up @@ -2545,12 +2545,11 @@ public function processExprNode(
ExpressionContext $context,
): ExpressionResult
{
$existingExprResult = $storage->findResult($expr);
if ($existingExprResult !== null) {
if ($nodeCallback instanceof ShallowNodeCallback) {
return $existingExprResult;
$existingBeforeScope = $storage->findBeforeScope($expr);
if ($existingBeforeScope !== null) {
if (!$nodeCallback instanceof ShallowNodeCallback) {
throw new ShouldNotHappenException(sprintf('Expr %s on line %d has already been analysed', get_class($expr), $expr->getStartLine()));
}
throw new ShouldNotHappenException(sprintf('Expr %s on line %d has already been analysed', get_class($expr), $expr->getStartLine()));
}

if ($expr instanceof Expr\CallLike && $expr->isFirstClassCallable()) {
Expand All @@ -2567,7 +2566,7 @@ public function processExprNode(
}

$newExprResult = $this->processExprNode($stmt, $newExpr, $scope, $storage, $nodeCallback, $context);
$this->storeResult($storage, $expr, $newExprResult);
$this->storeBeforeScope($storage, $expr, $newExprResult);
return $newExprResult;
}

Expand Down Expand Up @@ -2639,7 +2638,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
}

$result = new ExpressionResult($scope, $beforeScope, $hasYield, $isAlwaysTerminating, $throwPoints, $impurePoints);
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);

return $result;
},
Expand Down Expand Up @@ -2696,7 +2695,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
$result->getThrowPoints(),
$result->getImpurePoints(),
);
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);

return $result;
}
Expand All @@ -2707,7 +2706,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
);
$scope = $result->getScope();
if (!$expr instanceof Expr\AssignOp\Coalesce) {
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);
}
$hasYield = $result->hasYield();
$throwPoints = $result->getThrowPoints();
Expand Down Expand Up @@ -3236,7 +3235,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
);
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);

return $result;
} elseif ($expr instanceof StaticCall) {
Expand Down Expand Up @@ -3456,7 +3455,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
);
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);

return $result;
} elseif ($expr instanceof StaticPropertyFetch) {
Expand Down Expand Up @@ -3501,7 +3500,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
[],
[],
);
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);

return $result;
} elseif ($expr instanceof Expr\ArrowFunction) {
Expand All @@ -3514,7 +3513,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
[],
[],
);
$this->storeResult($storage, $expr, $exprResult);
$this->storeBeforeScope($storage, $expr, $exprResult);
return $exprResult;
} elseif ($expr instanceof ErrorSuppress) {
$result = $this->processExprNode($stmt, $expr->expr, $scope, $storage, $nodeCallback, $context);
Expand Down Expand Up @@ -3623,7 +3622,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
static fn (): MutatingScope => $rightResult->getScope()->filterByTruthyValue($expr),
static fn (): MutatingScope => $leftMergedWithRightScope->filterByFalseyValue($expr),
);
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);
return $result;
} elseif ($expr instanceof BooleanOr || $expr instanceof BinaryOp\LogicalOr) {
$leftResult = $this->processExprNode($stmt, $expr->left, $scope, $storage, $nodeCallback, $context->enterDeep());
Expand All @@ -3647,7 +3646,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
static fn (): MutatingScope => $leftMergedWithRightScope->filterByTruthyValue($expr),
static fn (): MutatingScope => $rightResult->getScope()->filterByFalseyValue($expr),
);
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);
return $result;
} elseif ($expr instanceof Coalesce) {
$nonNullabilityResult = $this->ensureNonNullability($scope, $expr->left);
Expand Down Expand Up @@ -4121,7 +4120,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
static fn (): MutatingScope => $finalScope->filterByTruthyValue($expr),
static fn (): MutatingScope => $finalScope->filterByFalseyValue($expr),
);
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);
return $result;

} elseif ($expr instanceof Expr\Yield_) {
Expand Down Expand Up @@ -4477,7 +4476,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
static fn (): MutatingScope => $scope->filterByTruthyValue($expr),
static fn (): MutatingScope => $scope->filterByFalseyValue($expr),
);
$this->storeResult($storage, $expr, $result);
$this->storeBeforeScope($storage, $expr, $result);
return $result;
}

Expand Down Expand Up @@ -5668,7 +5667,7 @@ private function processArgs(
$isAlwaysTerminating = $isAlwaysTerminating || $closureResult->isAlwaysTerminating();
}

$this->storeResult($storage, $arg->value, new ExpressionResult($closureResult->getScope(), $scopeToPass, false, $isAlwaysTerminating, $throwPoints, $impurePoints));
$this->storeBeforeScope($storage, $arg->value, new ExpressionResult($closureResult->getScope(), $scopeToPass, false, $isAlwaysTerminating, $throwPoints, $impurePoints));

$uses = [];
foreach ($arg->value->uses as $use) {
Expand Down Expand Up @@ -5724,7 +5723,7 @@ private function processArgs(
$impurePoints = array_merge($impurePoints, $arrowFunctionResult->getImpurePoints());
$isAlwaysTerminating = $isAlwaysTerminating || $arrowFunctionResult->isAlwaysTerminating();
}
$this->storeResult($storage, $arg->value, new ExpressionResult($arrowFunctionResult->getScope(), $scopeToPass, false, $isAlwaysTerminating, $throwPoints, $impurePoints));
$this->storeBeforeScope($storage, $arg->value, new ExpressionResult($arrowFunctionResult->getScope(), $scopeToPass, false, $isAlwaysTerminating, $throwPoints, $impurePoints));
} else {
$exprType = $scope->getType($arg->value);
$exprResult = $this->processExprNode($stmt, $arg->value, $scopeToPass, $storage, $nodeCallback, $context->enterDeep());
Expand Down
Loading