Skip to content
Draft
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
49 changes: 38 additions & 11 deletions src/Analyser/ExprHandler/ArrayDimFetchHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PhpParser\Node\Stmt;
use PHPStan\Analyser\ExpressionContext;
use PHPStan\Analyser\ExpressionResult;
use PHPStan\Analyser\ExpressionResultFactory;
use PHPStan\Analyser\ExpressionResultStorage;
use PHPStan\Analyser\ExprHandler;
use PHPStan\Analyser\ExprHandler\Helper\NullsafeShortCircuitingHelper;
Expand All @@ -30,6 +31,12 @@
final class ArrayDimFetchHandler implements ExprHandler
{

public function __construct(
private ExpressionResultFactory $expressionResultFactory,
)
{
}

public function supports(Expr $expr): bool
{
return $expr instanceof ArrayDimFetch;
Expand Down Expand Up @@ -75,8 +82,10 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$varResult = $nodeScopeResolver->processExprNode($stmt, $expr->var, $scope, $storage, $nodeCallback, $context->enterDeep());
$scope = $varResult->getScope();

return new ExpressionResult(
return $this->expressionResultFactory->create(
$expr,
$scope,
typeCallback: static fn () => new NeverType(),
hasYield: $varResult->hasYield(),
isAlwaysTerminating: $varResult->isAlwaysTerminating(),
throwPoints: $varResult->getThrowPoints(),
Expand All @@ -92,20 +101,38 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$impurePoints = array_merge($dimResult->getImpurePoints(), $varResult->getImpurePoints());
$scope = $varResult->getScope();

$varType = $scope->getType($expr->var);
$offsetGetResult = $nodeScopeResolver->processExprNode(
$stmt,
new MethodCall($expr->var, new Identifier('offsetGet'), [new Arg($expr->dim)]),
$scope,
new ExpressionResultStorage(),
new NoopNodeCallback(),
$context->enterDeep(),
);

$varType = $varResult->getType();
if (!$varType->isArray()->yes() && !(new ObjectType(ArrayAccess::class))->isSuperTypeOf($varType)->no()) {
$throwPoints = array_merge($throwPoints, $nodeScopeResolver->processExprNode(
$stmt,
new MethodCall($expr->var, 'offsetGet'),
$scope,
$storage,
new NoopNodeCallback(),
$context,
)->getThrowPoints());
$throwPoints = array_merge($throwPoints, $offsetGetResult->getThrowPoints());
}

return new ExpressionResult(
return $this->expressionResultFactory->create(
$expr,
$scope,
typeCallback: static function (Expr $uninteresting, MutatingScope $scope) use ($varResult, $dimResult, $offsetGetResult): Type {
$varType = $varResult->getTypeForScope($scope);
if ($varType instanceof NeverType) {
return $varType;
}

if (
!$varType->isArray()->yes()
&& (new ObjectType(ArrayAccess::class))->isSuperTypeOf($varType)->yes()
) {
return $offsetGetResult->getTypeForScope($scope);
}

return $varType->getOffsetValueType($dimResult->getTypeForScope($scope));
},
hasYield: $dimResult->hasYield() || $varResult->hasYield(),
isAlwaysTerminating: $dimResult->isAlwaysTerminating() || $varResult->isAlwaysTerminating(),
throwPoints: $throwPoints,
Expand Down
19 changes: 18 additions & 1 deletion src/Analyser/ExprHandler/ArrayHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
use PhpParser\Node\Stmt;
use PHPStan\Analyser\ExpressionContext;
use PHPStan\Analyser\ExpressionResult;
use PHPStan\Analyser\ExpressionResultFactory;
use PHPStan\Analyser\ExpressionResultStorage;
use PHPStan\Analyser\ExprHandler;
use PHPStan\Analyser\MutatingScope;
use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\Analyser\NoopNodeCallback;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Node\LiteralArrayItem;
use PHPStan\Node\LiteralArrayNode;
use PHPStan\Reflection\InitializerExprTypeResolver;
use PHPStan\Type\Type;
use function array_merge;
use function spl_object_id;

/**
* @implements ExprHandler<Array_>
Expand All @@ -26,6 +29,7 @@ final class ArrayHandler implements ExprHandler
{

public function __construct(
private ExpressionResultFactory $expressionResultFactory,
private InitializerExprTypeResolver $initializerExprTypeResolver,
)
{
Expand All @@ -48,11 +52,14 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$throwPoints = [];
$impurePoints = [];
$isAlwaysTerminating = false;
/** @var array<int, ExpressionResult> */
$itemResults = [];
foreach ($expr->items as $arrayItem) {
$itemNodes[] = new LiteralArrayItem($scope, $arrayItem);
$nodeScopeResolver->callNodeCallback($nodeCallback, $arrayItem, $scope, $storage);
if ($arrayItem->key !== null) {
$keyResult = $nodeScopeResolver->processExprNode($stmt, $arrayItem->key, $scope, $storage, $nodeCallback, $context->enterDeep());
$itemResults[spl_object_id($arrayItem->key)] = $keyResult;
$hasYield = $hasYield || $keyResult->hasYield();
$throwPoints = array_merge($throwPoints, $keyResult->getThrowPoints());
$impurePoints = array_merge($impurePoints, $keyResult->getImpurePoints());
Expand All @@ -61,6 +68,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
}

$valueResult = $nodeScopeResolver->processExprNode($stmt, $arrayItem->value, $scope, $storage, $nodeCallback, $context->enterDeep());
$itemResults[spl_object_id($arrayItem->value)] = $valueResult;
$hasYield = $hasYield || $valueResult->hasYield();
$throwPoints = array_merge($throwPoints, $valueResult->getThrowPoints());
$impurePoints = array_merge($impurePoints, $valueResult->getImpurePoints());
Expand All @@ -69,8 +77,17 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
}
$nodeScopeResolver->callNodeCallback($nodeCallback, new LiteralArrayNode($expr, $itemNodes), $scope, $storage);

return new ExpressionResult(
return $this->expressionResultFactory->create(
$expr,
$scope,
typeCallback: fn (Expr $expr, MutatingScope $scope) => $this->initializerExprTypeResolver->getArrayType($expr, static function (Expr $e) use ($itemResults, $scope, $nodeScopeResolver, $stmt): Type {
$id = spl_object_id($e);
if (isset($itemResults[$id])) {
return $itemResults[$id]->getTypeForScope($scope);
}

return $nodeScopeResolver->processExprNode($stmt, $e, $scope, new ExpressionResultStorage(), new NoopNodeCallback(), ExpressionContext::createDeep())->getTypeForScope($scope);
}),
hasYield: $hasYield,
isAlwaysTerminating: $isAlwaysTerminating,
throwPoints: $throwPoints,
Expand Down
6 changes: 5 additions & 1 deletion src/Analyser/ExprHandler/ArrowFunctionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PhpParser\Node\Stmt;
use PHPStan\Analyser\ExpressionContext;
use PHPStan\Analyser\ExpressionResult;
use PHPStan\Analyser\ExpressionResultFactory;
use PHPStan\Analyser\ExpressionResultStorage;
use PHPStan\Analyser\ExprHandler;
use PHPStan\Analyser\ExprHandler\Helper\ClosureTypeResolver;
Expand All @@ -23,6 +24,7 @@ final class ArrowFunctionHandler implements ExprHandler
{

public function __construct(
private ExpressionResultFactory $expressionResultFactory,
private ClosureTypeResolver $closureTypeResolver,
)
{
Expand All @@ -37,8 +39,10 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
{
$result = $nodeScopeResolver->processArrowFunctionNode($stmt, $expr, $scope, $storage, $nodeCallback, null);

return new ExpressionResult(
return $this->expressionResultFactory->create(
$expr,
$result->getScope(),
typeCallback: static fn (Expr $uninteresting, MutatingScope $scope) => $result->getTypeForScope($scope),
hasYield: $result->hasYield(),
isAlwaysTerminating: false,
throwPoints: [],
Expand Down
21 changes: 16 additions & 5 deletions src/Analyser/ExprHandler/AssignHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use PHPStan\Analyser\ConditionalExpressionHolder;
use PHPStan\Analyser\ExpressionContext;
use PHPStan\Analyser\ExpressionResult;
use PHPStan\Analyser\ExpressionResultFactory;
use PHPStan\Analyser\ExpressionResultStorage;
use PHPStan\Analyser\ExpressionTypeHolder;
use PHPStan\Analyser\ExprHandler;
Expand Down Expand Up @@ -78,6 +79,7 @@ final class AssignHandler implements ExprHandler
{

public function __construct(
private ExpressionResultFactory $expressionResultFactory,
private TypeSpecifier $typeSpecifier,
private PhpVersion $phpVersion,
)
Expand Down Expand Up @@ -105,7 +107,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$expr->expr,
$nodeCallback,
$context,
static function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $storage, $nodeScopeResolver): ExpressionResult {
function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $storage, $nodeScopeResolver): ExpressionResult {
$impurePoints = [];
if ($expr instanceof AssignRef) {
$referencedExpr = $expr->expr;
Expand Down Expand Up @@ -145,7 +147,7 @@ static function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $contex
$scope = $scope->exitExpressionAssign($expr->expr);
}

return new ExpressionResult($scope, $hasYield, $isAlwaysTerminating, $throwPoints, $impurePoints);
return $this->expressionResultFactory->create($expr->expr, $scope, typeCallback: static fn (Expr $uninteresting, MutatingScope $scope) => $result->getTypeForScope($scope), hasYield: $hasYield, isAlwaysTerminating: $isAlwaysTerminating, throwPoints: $throwPoints, impurePoints: $impurePoints);
},
true,
);
Expand All @@ -159,8 +161,10 @@ static function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $contex
}
}

return new ExpressionResult(
return $this->expressionResultFactory->create(
$expr->expr,
$scope,
typeCallback: static fn (Expr $uninteresting, MutatingScope $scope) => $result->getTypeForScope($scope),
hasYield: $result->hasYield(),
isAlwaysTerminating: $result->isAlwaysTerminating(),
throwPoints: $result->getThrowPoints(),
Expand Down Expand Up @@ -192,10 +196,12 @@ public function processAssignVar(
$throwPoints = [];
$impurePoints = [];
$isAlwaysTerminating = false;
$exprCallbackResult = null;
$isAssignOp = $assignedExpr instanceof Expr\AssignOp && !$enterExpressionAssign;
if ($var instanceof Variable) {
$nodeScopeResolver->storeBeforeScope($storage, $var, $scope);
$result = $processExprCallback($scope);
$exprCallbackResult = $result;
$hasYield = $result->hasYield();
$throwPoints = $result->getThrowPoints();
$impurePoints = $result->getImpurePoints();
Expand Down Expand Up @@ -382,6 +388,7 @@ public function processAssignVar(

// 3. eval assigned expr
$result = $processExprCallback($scope);
$exprCallbackResult = $result;
$hasYield = $hasYield || $result->hasYield();
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
Expand Down Expand Up @@ -508,6 +515,7 @@ public function processAssignVar(

$scopeBeforeAssignEval = $scope;
$result = $processExprCallback($scope);
$exprCallbackResult = $result;
$hasYield = $hasYield || $result->hasYield();
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
Expand Down Expand Up @@ -621,6 +629,7 @@ public function processAssignVar(

$scopeBeforeAssignEval = $scope;
$result = $processExprCallback($scope);
$exprCallbackResult = $result;
$hasYield = $hasYield || $result->hasYield();
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
Expand Down Expand Up @@ -667,6 +676,7 @@ public function processAssignVar(
} elseif ($var instanceof List_) {
$nodeScopeResolver->storeBeforeScope($storage, $var, $scope);
$result = $processExprCallback($scope);
$exprCallbackResult = $result;
$hasYield = $result->hasYield();
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
Expand Down Expand Up @@ -706,7 +716,7 @@ public function processAssignVar(
new GetOffsetValueTypeExpr($assignedExpr, $dimExpr),
$nodeCallback,
$context,
static fn (MutatingScope $scope): ExpressionResult => new ExpressionResult($scope, hasYield: false, isAlwaysTerminating: false, throwPoints: [], impurePoints: []),
fn (MutatingScope $scope): ExpressionResult => $this->expressionResultFactory->create($arrayItem->value, $scope, typeCallback: static fn () => new MixedType(), hasYield: false, isAlwaysTerminating: false, throwPoints: [], impurePoints: []),
$enterExpressionAssign,
);
$scope = $result->getScope();
Expand Down Expand Up @@ -790,6 +800,7 @@ public function processAssignVar(
$isAlwaysTerminating = $varResult->isAlwaysTerminating();
$scope = $varResult->getScope();
$result = $processExprCallback($scope);
$exprCallbackResult = $result;
$hasYield = $hasYield || $result->hasYield();
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
Expand All @@ -798,7 +809,7 @@ public function processAssignVar(
}

// stored where processAssignVar is called
return new ExpressionResult($scope, $hasYield, $isAlwaysTerminating, $throwPoints, $impurePoints);
return $this->expressionResultFactory->create($assignedExpr, $scope, typeCallback: static fn (Expr $uninteresting, MutatingScope $scope) => $exprCallbackResult->getTypeForScope($scope), hasYield: $hasYield, isAlwaysTerminating: $isAlwaysTerminating, throwPoints: $throwPoints, impurePoints: $impurePoints);
}

private function unwrapAssign(Expr $expr): Expr
Expand Down
Loading