From f9277dd95f523acd8b1c673ff7c8ec7cca927e02 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 1 Feb 2022 16:47:16 +0100 Subject: [PATCH 1/5] modulo '1' is always zero --- src/Analyser/MutatingScope.php | 26 ++++++++++++++----- .../PHPStan/Analyser/data/modulo-operator.php | 22 ++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index fa70cc917a..72dda7500f 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 6e432eb259..0f2395a28b 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); + } } From 1ddeddc8b7a81a21d6b7b9a582a9dcbc5ebdc39f Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 1 Feb 2022 17:38:45 +0100 Subject: [PATCH 2/5] partly revert automated rename --- src/Analyser/MutatingScope.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 72dda7500f..6a5f069d97 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -1090,15 +1090,15 @@ private function resolveType(Expr $node): Type } $leftTypes = TypeUtils::getConstantScalars($this->getType($left)); - $rightScalarTypes = TypeUtils::getConstantScalars($this->getType($right)); + $rightTypes = TypeUtils::getConstantScalars($this->getType($right)); $leftTypesCount = count($leftTypes); - $rightTypesCount = count($rightScalarTypes); + $rightTypesCount = count($rightTypes); if ($leftTypesCount > 0 && $rightTypesCount > 0) { $resultTypes = []; $generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT; foreach ($leftTypes as $leftType) { - foreach ($rightScalarTypes as $rightType) { + foreach ($rightTypes as $rightType) { $resultType = $this->calculateFromScalars($node, $leftType, $rightType); if ($generalize) { $resultType = TypeUtils::generalizeType($resultType, GeneralizePrecision::lessSpecific()); From cbc90ec0dcc83d173f4c1c5883b308b8ea5d2ab1 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 1 Feb 2022 20:45:36 +0100 Subject: [PATCH 3/5] cover 1.0 --- src/Analyser/MutatingScope.php | 4 ++-- tests/PHPStan/Analyser/data/modulo-operator.php | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 6a5f069d97..7acab4ce1f 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -1053,12 +1053,12 @@ private function resolveType(Expr $node): Type } $rightType = $this->getType($right); - $stringType = $rightType->toString(); + $integerType = $rightType->toInteger(); if ( $node instanceof Node\Expr\BinaryOp\Mod || $node instanceof Node\Expr\AssignOp\Mod ) { - if ($stringType instanceof ConstantScalarType && $stringType->getValue() === '1') { + if ($integerType instanceof ConstantIntegerType && $integerType->getValue() === 1) { return new ConstantIntegerType(0); } } diff --git a/tests/PHPStan/Analyser/data/modulo-operator.php b/tests/PHPStan/Analyser/data/modulo-operator.php index 0f2395a28b..50a8083b68 100644 --- a/tests/PHPStan/Analyser/data/modulo-operator.php +++ b/tests/PHPStan/Analyser/data/modulo-operator.php @@ -56,6 +56,9 @@ function moduleOne(int $i, float $f) { assertType('0', '1' % '1'); assertType('0', 1.24 % '1'); + assertType('0', $i % '1.0'); + assertType('0', $f % '1.0'); + assertType('0', $i % '1'); assertType('0', $f % '1'); From 2c0306e9503369d16db5845a7d8ab09cfc85fb7b Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 1 Feb 2022 20:49:52 +0100 Subject: [PATCH 4/5] cover float 1.0 --- tests/PHPStan/Analyser/data/modulo-operator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/PHPStan/Analyser/data/modulo-operator.php b/tests/PHPStan/Analyser/data/modulo-operator.php index 50a8083b68..a4876e59f1 100644 --- a/tests/PHPStan/Analyser/data/modulo-operator.php +++ b/tests/PHPStan/Analyser/data/modulo-operator.php @@ -56,6 +56,9 @@ function moduleOne(int $i, float $f) { assertType('0', '1' % '1'); assertType('0', 1.24 % '1'); + assertType('0', $i % 1.0); + assertType('0', $f % 1.0); + assertType('0', $i % '1.0'); assertType('0', $f % '1.0'); From 578e96da0abc7ed7c9090814b786cef190f7884f Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 1 Feb 2022 21:02:57 +0100 Subject: [PATCH 5/5] cover division by false-values --- tests/PHPStan/Analyser/data/div-by-zero.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/PHPStan/Analyser/data/div-by-zero.php b/tests/PHPStan/Analyser/data/div-by-zero.php index 0c38a50c10..2dae4d4767 100644 --- a/tests/PHPStan/Analyser/data/div-by-zero.php +++ b/tests/PHPStan/Analyser/data/div-by-zero.php @@ -17,7 +17,12 @@ public function doFoo(int $range1, int $range2, int $int): void assertType('(float|int)', 5 / $range2); assertType('(float|int)', $range1 / $range2); assertType('(float|int)', 5 / $int); + assertType('*ERROR*', 5 / 0); + assertType('*ERROR*', 5 / '0'); + assertType('*ERROR*', 5 / 0.0); + assertType('*ERROR*', 5 / false); + assertType('*ERROR*', 5 / null); } }