Skip to content

Commit f4a9412

Browse files
committed
BinaryPlusHandler
1 parent a564153 commit f4a9412

File tree

3 files changed

+74
-0
lines changed

3 files changed

+74
-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\Expr\BinaryOp\Plus;
8+
use PhpParser\Node\Stmt;
9+
use PHPStan\Analyser\ExpressionContext;
10+
use PHPStan\Analyser\Generator\ExprAnalysisRequest;
11+
use PHPStan\Analyser\Generator\ExprAnalysisResult;
12+
use PHPStan\Analyser\Generator\ExprHandler;
13+
use PHPStan\Analyser\Generator\GeneratorScope;
14+
use PHPStan\Analyser\SpecifiedTypes;
15+
use PHPStan\DependencyInjection\AutowiredService;
16+
use PHPStan\Reflection\InitializerExprTypeResolver;
17+
use function array_merge;
18+
19+
/**
20+
* @implements ExprHandler<Plus>
21+
*/
22+
#[AutowiredService]
23+
final class BinaryPlusHandler implements ExprHandler
24+
{
25+
26+
public function __construct(private InitializerExprTypeResolver $initializerExprTypeResolver)
27+
{
28+
}
29+
30+
public function supports(Expr $expr): bool
31+
{
32+
return $expr instanceof Plus;
33+
}
34+
35+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context, ?callable $alternativeNodeCallback): Generator
36+
{
37+
$leftResult = yield new ExprAnalysisRequest($stmt, $expr->left, $scope, $context, $alternativeNodeCallback);
38+
$rightResult = yield new ExprAnalysisRequest($stmt, $expr->right, $leftResult->scope, $context, $alternativeNodeCallback);
39+
40+
return new ExprAnalysisResult(
41+
$this->initializerExprTypeResolver->getPlusTypeFromTypes($expr->left, $expr->right, $leftResult->type, $rightResult->type),
42+
$this->initializerExprTypeResolver->getPlusTypeFromTypes($expr->left, $expr->right, $leftResult->nativeType, $rightResult->nativeType),
43+
$rightResult->scope,
44+
hasYield: $leftResult->hasYield || $rightResult->hasYield,
45+
isAlwaysTerminating: $leftResult->isAlwaysTerminating || $rightResult->isAlwaysTerminating,
46+
throwPoints: array_merge($leftResult->throwPoints, $rightResult->throwPoints),
47+
impurePoints: array_merge($leftResult->impurePoints, $rightResult->impurePoints),
48+
specifiedTruthyTypes: new SpecifiedTypes(),
49+
specifiedFalseyTypes: new SpecifiedTypes(),
50+
);
51+
}
52+
53+
}

src/Reflection/InitializerExprTypeResolver.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,11 @@ public function getPlusType(Expr $left, Expr $right, callable $getTypeCallback):
13371337
$leftType = $getTypeCallback($left);
13381338
$rightType = $getTypeCallback($right);
13391339

1340+
return $this->getPlusTypeFromTypes($left, $right, $leftType, $rightType);
1341+
}
1342+
1343+
public function getPlusTypeFromTypes(Expr $left, Expr $right, Type $leftType, Type $rightType): Type
1344+
{
13401345
if ($leftType instanceof NeverType || $rightType instanceof NeverType) {
13411346
return $this->getNeverType($leftType, $rightType);
13421347
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace GeneratorNodeScopeResolverTest;
44

5+
use function PHPStan\Testing\assertNativeType;
56
use function PHPStan\Testing\assertType;
67

78
class Foo
@@ -18,6 +19,21 @@ public function doImplicitArrayCreation(): void
1819
assertType('array{bla: 1}', $a);
1920
}
2021

22+
/**
23+
* @param int $a
24+
* @param int $b
25+
* @return void
26+
*/
27+
public function doPlus($a, $b, int $c, int $d): void
28+
{
29+
assertType('int', $a + $b);
30+
assertNativeType('(array|float|int)', $a + $b);
31+
assertType('2', 1 + 1);
32+
assertNativeType('2', 1 + 1);
33+
assertType('int', $c + $d);
34+
assertNativeType('int', $c + $d);
35+
}
36+
2137
}
2238

2339
function (): void {

0 commit comments

Comments
 (0)