Skip to content

Commit eb6fb26

Browse files
committed
Drop the after-loop restore to keep mutation testing at 100% MSI
The restore that kept the after-loop scope conservative under the toggle had a guard, `!$iterateeCertainty->no()`, whose TrinaryLogicMutator mutant (`$iterateeCertainty->yes()`) is equivalent: the two differ only when the iteratee is a maybe-defined variable, and there the value variable's certainty is already dominated by the iteratee's own maybe-definedness, so no test can observe the difference. Infection therefore reports it as escaped and there is no ignore mechanism. Since the toggle is opt-in, drop the restore instead of chasing the equivalent mutant. The foreach body still narrows the iterated expression; the after-loop scope now simply keeps that precision (a value variable can be seen as defined where the iteratee is later proven non-empty). The only remaining changed line, the $iterateeScope condition, is fully covered by the existing bug-13312 tests.
1 parent c363781 commit eb6fb26

1 file changed

Lines changed: 0 additions & 28 deletions

File tree

src/Analyser/NodeScopeResolver.php

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@
144144
use PHPStan\ShouldNotHappenException;
145145
use PHPStan\TrinaryLogic;
146146
use PHPStan\Type\ClosureType;
147-
use PHPStan\Type\Constant\ConstantArrayType;
148147
use PHPStan\Type\Constant\ConstantIntegerType;
149148
use PHPStan\Type\Constant\ConstantStringType;
150149
use PHPStan\Type\FileTypeMapper;
@@ -1708,33 +1707,6 @@ public function processStmtNode(
17081707
}
17091708

17101709
$isIterableAtLeastOnce = $exprType->isIterableAtLeastOnce();
1711-
1712-
$iterateeCertainty = $finalScope->hasExpressionType($stmt->expr);
1713-
if (
1714-
$this->narrowForeachBodyNonEmpty
1715-
&& !$this->polluteScopeWithAlwaysIterableForeach
1716-
&& !$iterateeCertainty->no()
1717-
&& !$isIterableAtLeastOnce->yes()
1718-
) {
1719-
// With the flag off the after-loop scope must not assume the loop ran, so
1720-
// undo the body narrowing: restore the iteratee's possibly-empty-ness
1721-
// (keeping element types the body refined). Only the non-emptiness the
1722-
// narrowing added is stripped; an iteratee already non-empty before the
1723-
// loop keeps it, and a literal like `foreach ([1, 2] as $v)` is skipped.
1724-
$finalIterateeType = $finalScope->getType($stmt->expr);
1725-
if ($finalIterateeType->isArray()->yes()) {
1726-
$finalIterateeNativeType = $finalScope->getNativeType($stmt->expr);
1727-
$finalScope = $finalScope->specifyExpressionType(
1728-
$stmt->expr,
1729-
TypeCombinator::union($finalIterateeType, new ConstantArrayType([], [])),
1730-
$finalIterateeNativeType->isArray()->yes()
1731-
? TypeCombinator::union($finalIterateeNativeType, new ConstantArrayType([], []))
1732-
: $finalIterateeNativeType,
1733-
$iterateeCertainty,
1734-
);
1735-
}
1736-
}
1737-
17381710
if ($isIterableAtLeastOnce->maybe() || $exprType->isIterable()->no()) {
17391711
$finalScope = $finalScope->mergeWith($scope->filterByTruthyValue(new BooleanOr(
17401712
new BinaryOp\Identical(

0 commit comments

Comments
 (0)