Skip to content

Commit a1feba2

Browse files
phpstan-botclaude
andcommitted
Merge sub-expression side effect checks into existing purity conditions
Instead of having a standalone subExpressionsHaveSideEffects check separate from the existing MethodCall/StaticCall/FuncCall purity conditions, merge the receiver chain and argument side effect checks directly into those conditions. This addresses the review feedback that the hasSideEffects() checking pattern was duplicated. - MethodCall block: now also checks receiver ($expr->var) and args - StaticCall block: now also checks class expr and args - FuncCall blocks: now also check args for side effects - Extract callLikeArgsHaveSideEffects() helper, reused by both the existing blocks and subExpressionsHaveSideEffects() - subExpressionsHaveSideEffects() remains as catch-all for types not covered by the blocks above (PropertyFetch, ArrayDimFetch, NullsafeMethodCall, NullsafePropertyFetch, StaticPropertyFetch) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 96472f1 commit a1feba2

1 file changed

Lines changed: 28 additions & 5 deletions

File tree

src/Analyser/TypeSpecifier.php

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,6 +2334,10 @@ private function createForExpr(
23342334
if (!$this->rememberPossiblyImpureFunctionValues && !$hasSideEffects->no()) {
23352335
return new SpecifiedTypes([], []);
23362336
}
2337+
2338+
if ($this->callLikeArgsHaveSideEffects($expr, $scope)) {
2339+
return new SpecifiedTypes([], []);
2340+
}
23372341
}
23382342

23392343
if (
@@ -2358,6 +2362,10 @@ private function createForExpr(
23582362
}
23592363
}
23602364
}
2365+
2366+
if ($this->callLikeArgsHaveSideEffects($expr, $scope)) {
2367+
return new SpecifiedTypes([], []);
2368+
}
23612369
}
23622370

23632371
if (
@@ -2371,6 +2379,8 @@ private function createForExpr(
23712379
$methodReflection === null
23722380
|| $methodReflection->hasSideEffects()->yes()
23732381
|| (!$this->rememberPossiblyImpureFunctionValues && !$methodReflection->hasSideEffects()->no())
2382+
|| $this->expressionHasSideEffects($expr->var, $scope)
2383+
|| $this->callLikeArgsHaveSideEffects($expr, $scope)
23742384
) {
23752385
if (isset($containsNull) && !$containsNull) {
23762386
return $this->createNullsafeTypes($originalExpr, $scope, $context, $type);
@@ -2396,6 +2406,8 @@ private function createForExpr(
23962406
$methodReflection === null
23972407
|| $methodReflection->hasSideEffects()->yes()
23982408
|| (!$this->rememberPossiblyImpureFunctionValues && !$methodReflection->hasSideEffects()->no())
2409+
|| ($expr->class instanceof Expr && $this->expressionHasSideEffects($expr->class, $scope))
2410+
|| $this->callLikeArgsHaveSideEffects($expr, $scope)
23992411
) {
24002412
if (isset($containsNull) && !$containsNull) {
24012413
return $this->createNullsafeTypes($originalExpr, $scope, $context, $type);
@@ -2462,11 +2474,22 @@ private function subExpressionsHaveSideEffects(Expr $expr, Scope $scope): bool
24622474
}
24632475
}
24642476

2465-
if ($expr instanceof Expr\CallLike && !$expr->isFirstClassCallable()) {
2466-
foreach ($expr->getArgs() as $arg) {
2467-
if ($this->expressionHasSideEffects($arg->value, $scope)) {
2468-
return true;
2469-
}
2477+
if ($expr instanceof Expr\CallLike && $this->callLikeArgsHaveSideEffects($expr, $scope)) {
2478+
return true;
2479+
}
2480+
2481+
return false;
2482+
}
2483+
2484+
private function callLikeArgsHaveSideEffects(Expr\CallLike $expr, Scope $scope): bool
2485+
{
2486+
if ($expr->isFirstClassCallable()) {
2487+
return false;
2488+
}
2489+
2490+
foreach ($expr->getArgs() as $arg) {
2491+
if ($this->expressionHasSideEffects($arg->value, $scope)) {
2492+
return true;
24702493
}
24712494
}
24722495

0 commit comments

Comments
 (0)