diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index fa70cc917a0..72dda7500f0 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -1051,12 +1051,24 @@ private function resolveType(Expr $node): Type } else { $right = $node->right; } + $rightType = $this->getType($right); + + $stringType = $rightType->toString(); + if ( + $node instanceof Node\Expr\BinaryOp\Mod + || $node instanceof Node\Expr\AssignOp\Mod + ) { + if ($stringType instanceof ConstantScalarType && $stringType->getValue() === '1') { + return new ConstantIntegerType(0); + } + } + + $rightScalarTypes = TypeUtils::getConstantScalars($rightType->toNumber()); + foreach ($rightScalarTypes as $scalarType) { - $rightTypes = TypeUtils::getConstantScalars($this->getType($right)->toNumber()); - foreach ($rightTypes as $rightType) { if ( - $rightType->getValue() === 0 - || $rightType->getValue() === 0.0 + $scalarType->getValue() === 0 + || $scalarType->getValue() === 0.0 ) { return new ErrorType(); } @@ -1078,15 +1090,15 @@ private function resolveType(Expr $node): Type } $leftTypes = TypeUtils::getConstantScalars($this->getType($left)); - $rightTypes = TypeUtils::getConstantScalars($this->getType($right)); + $rightScalarTypes = TypeUtils::getConstantScalars($this->getType($right)); $leftTypesCount = count($leftTypes); - $rightTypesCount = count($rightTypes); + $rightTypesCount = count($rightScalarTypes); if ($leftTypesCount > 0 && $rightTypesCount > 0) { $resultTypes = []; $generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT; foreach ($leftTypes as $leftType) { - foreach ($rightTypes as $rightType) { + foreach ($rightScalarTypes as $rightType) { $resultType = $this->calculateFromScalars($node, $leftType, $rightType); if ($generalize) { $resultType = TypeUtils::generalizeType($resultType, GeneralizePrecision::lessSpecific()); diff --git a/tests/PHPStan/Analyser/data/modulo-operator.php b/tests/PHPStan/Analyser/data/modulo-operator.php index 6e432eb2599..0f2395a28b4 100644 --- a/tests/PHPStan/Analyser/data/modulo-operator.php +++ b/tests/PHPStan/Analyser/data/modulo-operator.php @@ -45,4 +45,26 @@ function doBar(int $i, int $j, $p, $range, $zeroOrMore, $intConst, $unionRange, assertType('int', $j % $i); } + + function moduleOne(int $i, float $f) { + assertType('0', true % '1'); + assertType('0', false % '1'); + assertType('0', null % '1'); + assertType('0', -1 % '1'); + assertType('0', 0 % '1'); + assertType('0', 1 % '1'); + assertType('0', '1' % '1'); + assertType('0', 1.24 % '1'); + + assertType('0', $i % '1'); + assertType('0', $f % '1'); + + assertType('0', $i % true); + assertType('0', $f % true); + + $i %= '1'; + $f %= '1'; + assertType('0', $i); + assertType('0', $f); + } }