Skip to content
Draft
275 changes: 275 additions & 0 deletions NEW_WORLD.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ parameters:
-
rawMessage: Casting to string something that's already string.
identifier: cast.useless
count: 3
count: 5
path: src/Analyser/MutatingScope.php

-
Expand Down
24 changes: 24 additions & 0 deletions src/Analyser/DirectInternalScopeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function __construct(
private $nodeCallback,
private ConstantResolver $constantResolver,
private bool $fiber = false,
private bool $resultAware = false,
)
{
}
Expand All @@ -64,6 +65,8 @@ public function create(
$className = MutatingScope::class;
if ($this->fiber) {
$className = FiberScope::class;
} elseif ($this->resultAware) {
$className = ResultAwareScope::class;
}

return new $className(
Expand Down Expand Up @@ -120,6 +123,27 @@ public function toFiberFactory(): InternalScopeFactory
);
}

public function toResultAwareFactory(): InternalScopeFactory
{
return new self(
$this->container,
$this->reflectionProvider,
$this->initializerExprTypeResolver,
$this->expressionTypeResolverExtensionRegistryProvider,
$this->exprPrinter,
$this->typeSpecifier,
$this->propertyReflectionFinder,
$this->parser,
$this->phpVersion,
$this->attributeReflectionFactory,
$this->configPhpVersion,
$this->nodeCallback,
$this->constantResolver,
false,
true,
);
}

public function toMutatingFactory(): InternalScopeFactory
{
return new self(
Expand Down
214 changes: 185 additions & 29 deletions src/Analyser/ExprHandler/AssignHandler.php

Large diffs are not rendered by default.

26 changes: 21 additions & 5 deletions src/Analyser/ExprHandler/AssignOpHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function supports(Expr $expr): bool

public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult
{
$rhsExprResult = null;
$assignResult = $this->assignHandler->processAssignVar(
$nodeScopeResolver,
$scope,
Expand All @@ -62,7 +63,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$expr,
$nodeCallback,
$context,
static function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $storage, $nodeScopeResolver): ExpressionResult {
static function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $storage, $nodeScopeResolver, &$rhsExprResult): ExpressionResult {
$originalScope = $scope;
if ($expr instanceof Expr\AssignOp\Coalesce) {
$scope = $scope->filterByFalseyValue(
Expand All @@ -71,6 +72,7 @@ static function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $contex
}

$exprResult = $nodeScopeResolver->processExprNode($stmt, $expr->expr, $scope, $storage, $nodeCallback, $context->enterDeep());
$rhsExprResult = $exprResult;
if ($expr instanceof Expr\AssignOp\Coalesce) {
$nodeScopeResolver->storeBeforeScope($storage, $expr, $originalScope);
$isAlwaysTerminating = $exprResult->isAlwaysTerminating() && $originalScope->getType($expr->var)->isNull()->yes();
Expand All @@ -80,10 +82,20 @@ static function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $contex
$isAlwaysTerminating,
$exprResult->getThrowPoints(),
$exprResult->getImpurePoints(),
expr: $expr,
);
}

return $exprResult;
// the assigned value of an AssignOp is the op result, not the right side —
// wrap so processAssignVar falls back to the (guarded) legacy type of $expr
return new ExpressionResult(
$exprResult->getScope(),
$exprResult->hasYield(),
$exprResult->isAlwaysTerminating(),
$exprResult->getThrowPoints(),
$exprResult->getImpurePoints(),
expr: $expr,
);
},
$expr instanceof Expr\AssignOp\Coalesce,
);
Expand All @@ -94,13 +106,17 @@ static function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $contex
$throwPoints = $assignResult->getThrowPoints();
$impurePoints = $assignResult->getImpurePoints();
if (
($expr instanceof Expr\AssignOp\Div || $expr instanceof Expr\AssignOp\Mod) &&
!$scope->getType($expr->expr)->toNumber()->isSuperTypeOf(new ConstantIntegerType(0))->no()
($expr instanceof Expr\AssignOp\Div || $expr instanceof Expr\AssignOp\Mod)
&& $rhsExprResult !== null
&& !$rhsExprResult->getType()->toNumber()->isSuperTypeOf(new ConstantIntegerType(0))->no()
) {
$throwPoints[] = InternalThrowPoint::createExplicit($scope, new ObjectType(DivisionByZeroError::class), $expr, false);
}
if ($expr instanceof Expr\AssignOp\Concat) {
$toStringResult = $this->implicitToStringCallHelper->processImplicitToStringCall($expr->expr, $scope);
if ($rhsExprResult === null) {
throw new ShouldNotHappenException();
}
$toStringResult = $this->implicitToStringCallHelper->processImplicitToStringCall($expr->expr, $rhsExprResult->getType(), $scope);
$throwPoints = array_merge($throwPoints, $toStringResult->getThrowPoints());
$impurePoints = array_merge($impurePoints, $toStringResult->getImpurePoints());
}
Expand Down
4 changes: 2 additions & 2 deletions src/Analyser/ExprHandler/BinaryOpHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$throwPoints[] = InternalThrowPoint::createExplicit($leftResult->getScope(), new ObjectType(DivisionByZeroError::class), $expr, false);
}
if ($expr instanceof BinaryOp\Concat) {
$leftToStringResult = $this->implicitToStringCallHelper->processImplicitToStringCall($expr->left, $scope);
$rightToStringResult = $this->implicitToStringCallHelper->processImplicitToStringCall($expr->right, $leftResult->getScope());
$leftToStringResult = $this->implicitToStringCallHelper->processImplicitToStringCall($expr->left, $leftResult->getType(), $scope);
$rightToStringResult = $this->implicitToStringCallHelper->processImplicitToStringCall($expr->right, $rightResult->getType(), $leftResult->getScope());
$throwPoints = array_merge($throwPoints, $leftToStringResult->getThrowPoints(), $rightToStringResult->getThrowPoints());
$impurePoints = array_merge($impurePoints, $leftToStringResult->getImpurePoints(), $rightToStringResult->getImpurePoints());
}
Expand Down
2 changes: 1 addition & 1 deletion src/Analyser/ExprHandler/CastStringHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$impurePoints = $exprResult->getImpurePoints();
$throwPoints = $exprResult->getThrowPoints();

$toStringResult = $this->implicitToStringCallHelper->processImplicitToStringCall($expr->expr, $scope);
$toStringResult = $this->implicitToStringCallHelper->processImplicitToStringCall($expr->expr, $exprResult->getType(), $scope);
$throwPoints = array_merge($throwPoints, $toStringResult->getThrowPoints());
$impurePoints = array_merge($impurePoints, $toStringResult->getImpurePoints());

Expand Down
Loading
Loading