diff --git a/src/Analyser/Generator/ExprHandler/BitwiseNotHandler.php b/src/Analyser/Generator/ExprHandler/BitwiseNotHandler.php new file mode 100644 index 0000000000..7ddc90221b --- /dev/null +++ b/src/Analyser/Generator/ExprHandler/BitwiseNotHandler.php @@ -0,0 +1,51 @@ + + */ +#[AutowiredService] +final class BitwiseNotHandler implements ExprHandler +{ + + public function __construct(private InitializerExprTypeResolver $initializerExprTypeResolver) + { + } + + public function supports(Expr $expr): bool + { + return $expr instanceof Expr\BitwiseNot; + } + + public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context, ?callable $alternativeNodeCallback): Generator + { + $result = yield new ExprAnalysisRequest($stmt, $expr->expr, $scope, $context->enterDeep(), $alternativeNodeCallback); + + return new ExprAnalysisResult( + $this->initializerExprTypeResolver->getBitwiseNotTypeFromType($result->type), + $this->initializerExprTypeResolver->getBitwiseNotTypeFromType($result->nativeType), + $result->scope, + hasYield: $result->hasYield, + isAlwaysTerminating: $result->isAlwaysTerminating, + throwPoints: $result->throwPoints, + impurePoints: $result->impurePoints, + specifiedTruthyTypes: new SpecifiedTypes(), + specifiedFalseyTypes: new SpecifiedTypes(), + specifiedNullTypes: new SpecifiedTypes(), + ); + } + +} diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index 3bd59c0b10..eec0aac329 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -2532,6 +2532,12 @@ public function getUnaryMinusType(Expr $expr, callable $getTypeCallback): Type public function getBitwiseNotType(Expr $expr, callable $getTypeCallback): Type { $exprType = $getTypeCallback($expr); + + return $this->getBitwiseNotTypeFromType($exprType); + } + + public function getBitwiseNotTypeFromType(Type $exprType): Type + { return TypeTraverser::map($exprType, static function (Type $type, callable $traverse): Type { if ($type instanceof UnionType || $type instanceof IntersectionType) { return $traverse($type); diff --git a/tests/PHPStan/Analyser/Generator/data/gnsr.php b/tests/PHPStan/Analyser/Generator/data/gnsr.php index 60e78e87ca..03002b9f85 100644 --- a/tests/PHPStan/Analyser/Generator/data/gnsr.php +++ b/tests/PHPStan/Analyser/Generator/data/gnsr.php @@ -87,6 +87,20 @@ public function doMinus($a, $b, int $c, int $d): void assertNativeType('int', $c - $d); } + /** + * @param int $a + * @return void + */ + public function doBitwiseNot($a, int $b): void + { + assertType('int', ~$a); + assertNativeType('int', ~$b); + assertType('int', ~1); + assertNativeType('int', ~1); + assertType('int', ~$b); + assertNativeType('int', ~$b); + } + /** * @param int $a * @param int $b