From 554b71742fe5d6bfee2426a8cffc7d172e672e87 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 29 Nov 2025 14:30:52 +0100 Subject: [PATCH 1/3] Implement BitwiseAndHandler --- .../ExprHandler/BitwiseAndHandler.php | 53 +++++++++++++++++++ .../InitializerExprTypeResolver.php | 5 ++ .../PHPStan/Analyser/Generator/data/gnsr.php | 15 ++++++ 3 files changed, 73 insertions(+) create mode 100644 src/Analyser/Generator/ExprHandler/BitwiseAndHandler.php diff --git a/src/Analyser/Generator/ExprHandler/BitwiseAndHandler.php b/src/Analyser/Generator/ExprHandler/BitwiseAndHandler.php new file mode 100644 index 0000000000..2dedc4e0eb --- /dev/null +++ b/src/Analyser/Generator/ExprHandler/BitwiseAndHandler.php @@ -0,0 +1,53 @@ + + */ +#[AutowiredService] +final class BitwiseAndHandler implements ExprHandler +{ + + public function __construct(private InitializerExprTypeResolver $initializerExprTypeResolver) + { + } + + public function supports(Expr $expr): bool + { + return $expr instanceof Expr\BinaryOp\BitwiseAnd; + } + + public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context, ?callable $alternativeNodeCallback): Generator + { + $leftResult = yield new ExprAnalysisRequest($stmt, $expr->left, $scope, $context, $alternativeNodeCallback); + $rightResult = yield new ExprAnalysisRequest($stmt, $expr->right, $leftResult->scope, $context, $alternativeNodeCallback); + + return new ExprAnalysisResult( + $this->initializerExprTypeResolver->getBitwiseAndTypeFromTypes($leftResult->type, $rightResult->type), + $this->initializerExprTypeResolver->getBitwiseAndTypeFromTypes($leftResult->nativeType, $rightResult->nativeType), + $rightResult->scope, + hasYield: $leftResult->hasYield || $rightResult->hasYield, + isAlwaysTerminating: $leftResult->isAlwaysTerminating || $rightResult->isAlwaysTerminating, + throwPoints: array_merge($leftResult->throwPoints, $rightResult->throwPoints), + impurePoints: array_merge($leftResult->impurePoints, $rightResult->impurePoints), + specifiedTruthyTypes: new SpecifiedTypes(), + specifiedFalseyTypes: new SpecifiedTypes(), + specifiedNullTypes: new SpecifiedTypes(), + ); + } + +} diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index 08279510d8..27ceac0a4a 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -954,6 +954,11 @@ public function getBitwiseAndType(Expr $left, Expr $right, callable $getTypeCall $leftType = $getTypeCallback($left); $rightType = $getTypeCallback($right); + return $this->getBitwiseAndTypeFromTypes($leftType, $rightType); + } + + public function getBitwiseAndTypeFromTypes(Type $leftType, Type $rightType): Type + { if ($leftType instanceof NeverType || $rightType instanceof NeverType) { return $this->getNeverType($leftType, $rightType); } diff --git a/tests/PHPStan/Analyser/Generator/data/gnsr.php b/tests/PHPStan/Analyser/Generator/data/gnsr.php index 151ff2e406..acfbf90cd9 100644 --- a/tests/PHPStan/Analyser/Generator/data/gnsr.php +++ b/tests/PHPStan/Analyser/Generator/data/gnsr.php @@ -67,6 +67,21 @@ public function doMinus($a, $b, int $c, int $d): void assertNativeType('int', $c - $d); } + /** + * @param int $a + * @param int $b + * @return void + */ + public function doBitwiseAnd($a, $b, int $c, int $d): void + { + assertType('int', $a & $b); + assertNativeType('(float|int)', $a & $b); + assertType('1', 1 & 1); + assertNativeType('1', 1 & 1); + assertType('int', $c & $d); + assertNativeType('int', $c & $d); + } + /** * @param int $a * @param int $b From 2eb4520bb361ffd45f61d8d0e29a1c7b88f8e569 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 29 Nov 2025 14:33:31 +0100 Subject: [PATCH 2/3] Update gnsr.php --- tests/PHPStan/Analyser/Generator/data/gnsr.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/Generator/data/gnsr.php b/tests/PHPStan/Analyser/Generator/data/gnsr.php index acfbf90cd9..01177b496f 100644 --- a/tests/PHPStan/Analyser/Generator/data/gnsr.php +++ b/tests/PHPStan/Analyser/Generator/data/gnsr.php @@ -90,7 +90,7 @@ public function doBitwiseAnd($a, $b, int $c, int $d): void public function doMul($a, $b, int $c, int $d): void { assertType('int', $a * $b); - assertNativeType('(float|int)', $a * $b); + assertNativeType('int', $a * $b); assertType('1', 1 * 1); assertNativeType('1', 1 * 1); assertType('int', $c * $d); From 27b00c3b322dabff97b82badb91f32e7e7044366 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 29 Nov 2025 14:35:08 +0100 Subject: [PATCH 3/3] Update gnsr.php --- tests/PHPStan/Analyser/Generator/data/gnsr.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PHPStan/Analyser/Generator/data/gnsr.php b/tests/PHPStan/Analyser/Generator/data/gnsr.php index 01177b496f..b915536172 100644 --- a/tests/PHPStan/Analyser/Generator/data/gnsr.php +++ b/tests/PHPStan/Analyser/Generator/data/gnsr.php @@ -75,7 +75,7 @@ public function doMinus($a, $b, int $c, int $d): void public function doBitwiseAnd($a, $b, int $c, int $d): void { assertType('int', $a & $b); - assertNativeType('(float|int)', $a & $b); + assertNativeType('int', $a & $b); assertType('1', 1 & 1); assertNativeType('1', 1 & 1); assertType('int', $c & $d); @@ -90,7 +90,7 @@ public function doBitwiseAnd($a, $b, int $c, int $d): void public function doMul($a, $b, int $c, int $d): void { assertType('int', $a * $b); - assertNativeType('int', $a * $b); + assertNativeType('(float|int)', $a * $b); assertType('1', 1 * 1); assertNativeType('1', 1 * 1); assertType('int', $c * $d);