Skip to content

Commit 667d56b

Browse files
phpstan-botclaude
authored 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 ac59fc8 commit 667d56b

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
@@ -2338,6 +2338,10 @@ private function createForExpr(
23382338
if (!$this->rememberPossiblyImpureFunctionValues && !$hasSideEffects->no()) {
23392339
return new SpecifiedTypes([], []);
23402340
}
2341+
2342+
if ($this->callLikeArgsHaveSideEffects($expr, $scope)) {
2343+
return new SpecifiedTypes([], []);
2344+
}
23412345
}
23422346

23432347
if (
@@ -2362,6 +2366,10 @@ private function createForExpr(
23622366
}
23632367
}
23642368
}
2369+
2370+
if ($this->callLikeArgsHaveSideEffects($expr, $scope)) {
2371+
return new SpecifiedTypes([], []);
2372+
}
23652373
}
23662374

23672375
if (
@@ -2375,6 +2383,8 @@ private function createForExpr(
23752383
$methodReflection === null
23762384
|| $methodReflection->hasSideEffects()->yes()
23772385
|| (!$this->rememberPossiblyImpureFunctionValues && !$methodReflection->hasSideEffects()->no())
2386+
|| $this->expressionHasSideEffects($expr->var, $scope)
2387+
|| $this->callLikeArgsHaveSideEffects($expr, $scope)
23782388
) {
23792389
if (isset($containsNull) && !$containsNull) {
23802390
return $this->createNullsafeTypes($originalExpr, $scope, $context, $type);
@@ -2400,6 +2410,8 @@ private function createForExpr(
24002410
$methodReflection === null
24012411
|| $methodReflection->hasSideEffects()->yes()
24022412
|| (!$this->rememberPossiblyImpureFunctionValues && !$methodReflection->hasSideEffects()->no())
2413+
|| ($expr->class instanceof Expr && $this->expressionHasSideEffects($expr->class, $scope))
2414+
|| $this->callLikeArgsHaveSideEffects($expr, $scope)
24032415
) {
24042416
if (isset($containsNull) && !$containsNull) {
24052417
return $this->createNullsafeTypes($originalExpr, $scope, $context, $type);
@@ -2466,11 +2478,22 @@ private function subExpressionsHaveSideEffects(Expr $expr, Scope $scope): bool
24662478
}
24672479
}
24682480

2469-
if ($expr instanceof Expr\CallLike && !$expr->isFirstClassCallable()) {
2470-
foreach ($expr->getArgs() as $arg) {
2471-
if ($this->expressionHasSideEffects($arg->value, $scope)) {
2472-
return true;
2473-
}
2481+
if ($expr instanceof Expr\CallLike && $this->callLikeArgsHaveSideEffects($expr, $scope)) {
2482+
return true;
2483+
}
2484+
2485+
return false;
2486+
}
2487+
2488+
private function callLikeArgsHaveSideEffects(Expr\CallLike $expr, Scope $scope): bool
2489+
{
2490+
if ($expr->isFirstClassCallable()) {
2491+
return false;
2492+
}
2493+
2494+
foreach ($expr->getArgs() as $arg) {
2495+
if ($this->expressionHasSideEffects($arg->value, $scope)) {
2496+
return true;
24742497
}
24752498
}
24762499

0 commit comments

Comments
 (0)