Skip to content

Commit c779961

Browse files
staabmondrejmirtes
authored andcommitted
Implement BinaryShiftLeftHandler, BinaryShiftRightHandler
1 parent c8635d2 commit c779961

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Generator\ExprHandler;
4+
5+
use Generator;
6+
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Stmt;
8+
use PHPStan\Analyser\ExpressionContext;
9+
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
10+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
11+
use PHPStan\Analyser\Generator\ExprHandler;
12+
use PHPStan\Analyser\Generator\GeneratorScope;
13+
use PHPStan\Analyser\SpecifiedTypes;
14+
use PHPStan\DependencyInjection\AutowiredService;
15+
use PHPStan\Reflection\InitializerExprTypeResolver;
16+
use function array_merge;
17+
18+
/**
19+
* @implements ExprHandler<Expr\BinaryOp\ShiftLeft>
20+
*/
21+
#[AutowiredService]
22+
final class BinaryShiftLeftHandler implements ExprHandler
23+
{
24+
25+
public function __construct(private InitializerExprTypeResolver $initializerExprTypeResolver)
26+
{
27+
}
28+
29+
public function supports(Expr $expr): bool
30+
{
31+
return $expr instanceof Expr\BinaryOp\ShiftLeft;
32+
}
33+
34+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context, ?callable $alternativeNodeCallback): Generator
35+
{
36+
$leftResult = yield new ExprAnalysisRequest($stmt, $expr->left, $scope, $context, $alternativeNodeCallback);
37+
$rightResult = yield new ExprAnalysisRequest($stmt, $expr->right, $leftResult->scope, $context, $alternativeNodeCallback);
38+
39+
return new ExprAnalysisResult(
40+
$this->initializerExprTypeResolver->getShiftLeftTypeFromTypes($expr->left, $expr->right, $leftResult->type, $rightResult->type),
41+
$this->initializerExprTypeResolver->getShiftLeftTypeFromTypes($expr->left, $expr->right, $leftResult->nativeType, $rightResult->nativeType),
42+
$rightResult->scope,
43+
hasYield: $leftResult->hasYield || $rightResult->hasYield,
44+
isAlwaysTerminating: $leftResult->isAlwaysTerminating || $rightResult->isAlwaysTerminating,
45+
throwPoints: array_merge($leftResult->throwPoints, $rightResult->throwPoints),
46+
impurePoints: array_merge($leftResult->impurePoints, $rightResult->impurePoints),
47+
specifiedTruthyTypes: new SpecifiedTypes(),
48+
specifiedFalseyTypes: new SpecifiedTypes(),
49+
specifiedNullTypes: new SpecifiedTypes(),
50+
);
51+
}
52+
53+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Generator\ExprHandler;
4+
5+
use Generator;
6+
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Stmt;
8+
use PHPStan\Analyser\ExpressionContext;
9+
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
10+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
11+
use PHPStan\Analyser\Generator\ExprHandler;
12+
use PHPStan\Analyser\Generator\GeneratorScope;
13+
use PHPStan\Analyser\SpecifiedTypes;
14+
use PHPStan\DependencyInjection\AutowiredService;
15+
use PHPStan\Reflection\InitializerExprTypeResolver;
16+
use function array_merge;
17+
18+
/**
19+
* @implements ExprHandler<Expr\BinaryOp\ShiftRight>
20+
*/
21+
#[AutowiredService]
22+
final class BinaryShiftRightHandler implements ExprHandler
23+
{
24+
25+
public function __construct(private InitializerExprTypeResolver $initializerExprTypeResolver)
26+
{
27+
}
28+
29+
public function supports(Expr $expr): bool
30+
{
31+
return $expr instanceof Expr\BinaryOp\ShiftRight;
32+
}
33+
34+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context, ?callable $alternativeNodeCallback): Generator
35+
{
36+
$leftResult = yield new ExprAnalysisRequest($stmt, $expr->left, $scope, $context, $alternativeNodeCallback);
37+
$rightResult = yield new ExprAnalysisRequest($stmt, $expr->right, $leftResult->scope, $context, $alternativeNodeCallback);
38+
39+
return new ExprAnalysisResult(
40+
$this->initializerExprTypeResolver->getShiftRightTypeFromTypes($expr->left, $expr->right, $leftResult->type, $rightResult->type),
41+
$this->initializerExprTypeResolver->getShiftRightTypeFromTypes($expr->left, $expr->right, $leftResult->nativeType, $rightResult->nativeType),
42+
$rightResult->scope,
43+
hasYield: $leftResult->hasYield || $rightResult->hasYield,
44+
isAlwaysTerminating: $leftResult->isAlwaysTerminating || $rightResult->isAlwaysTerminating,
45+
throwPoints: array_merge($leftResult->throwPoints, $rightResult->throwPoints),
46+
impurePoints: array_merge($leftResult->impurePoints, $rightResult->impurePoints),
47+
specifiedTruthyTypes: new SpecifiedTypes(),
48+
specifiedFalseyTypes: new SpecifiedTypes(),
49+
specifiedNullTypes: new SpecifiedTypes(),
50+
);
51+
}
52+
53+
}

src/Reflection/InitializerExprTypeResolver.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,6 +1690,11 @@ public function getShiftLeftType(Expr $left, Expr $right, callable $getTypeCallb
16901690
$leftType = $getTypeCallback($left);
16911691
$rightType = $getTypeCallback($right);
16921692

1693+
return $this->getShiftLeftTypeFromTypes($left, $right, $leftType, $rightType);
1694+
}
1695+
1696+
public function getShiftLeftTypeFromTypes(Expr $left, Expr $right, Type $leftType, Type $rightType): Type
1697+
{
16931698
if ($leftType instanceof NeverType || $rightType instanceof NeverType) {
16941699
return $this->getNeverType($leftType, $rightType);
16951700
}
@@ -1749,6 +1754,11 @@ public function getShiftRightType(Expr $left, Expr $right, callable $getTypeCall
17491754
$leftType = $getTypeCallback($left);
17501755
$rightType = $getTypeCallback($right);
17511756

1757+
return $this->getShiftRightTypeFromTypes($left, $right, $leftType, $rightType);
1758+
}
1759+
1760+
public function getShiftRightTypeFromTypes(Expr $left, Expr $right, Type $leftType, Type $rightType): Type
1761+
{
17521762
if ($leftType instanceof NeverType || $rightType instanceof NeverType) {
17531763
return $this->getNeverType($leftType, $rightType);
17541764
}

tests/PHPStan/Analyser/Generator/data/gnsr.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,35 @@ public function doConcat($a, $b, string $c, string $d): void
191191
assertNativeType('string', $c . $d);
192192
}
193193

194+
/**
195+
* @param int $a
196+
* @param int $b
197+
* @return void
198+
*/
199+
public function doShiftLeft($a, $b, int $c, int $d): void
200+
{
201+
assertType('int', $a << $b);
202+
assertNativeType('(float|int)', $a << $b);
203+
assertType('8', 1 << 3);
204+
assertNativeType('8', 1 << 3);
205+
assertType('int', $c << $d);
206+
assertNativeType('int', $c << $d);
207+
}
208+
209+
/**
210+
* @param int $a
211+
* @param int $b
212+
* @return void
213+
*/
214+
public function doShiftRight($a, $b, int $c, int $d): void
215+
{
216+
assertType('int', $a >> $b);
217+
assertNativeType('(float|int)', $a >> $b);
218+
assertType('0', 1 >> 3);
219+
assertNativeType('0', 1 >> 3);
220+
assertType('int', $c >> $d);
221+
assertNativeType('int', $c >> $d);
222+
}
194223
}
195224

196225
function (): void {

0 commit comments

Comments
 (0)