Skip to content

Commit d8fdb61

Browse files
authored
Implement UnaryMinusHandler
1 parent 61b221c commit d8fdb61

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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\Scalar\Int_;
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\Generator\NoopNodeCallback;
15+
use PHPStan\Analyser\SpecifiedTypes;
16+
use PHPStan\DependencyInjection\AutowiredService;
17+
use PHPStan\Reflection\InitializerExprTypeResolver;
18+
use PHPStan\Type\IntegerRangeType;
19+
20+
/**
21+
* @implements ExprHandler<Expr\UnaryMinus>
22+
*/
23+
#[AutowiredService]
24+
final class UnaryMinusHandler implements ExprHandler
25+
{
26+
27+
public function __construct(private InitializerExprTypeResolver $initializerExprTypeResolver)
28+
{
29+
}
30+
31+
public function supports(Expr $expr): bool
32+
{
33+
return $expr instanceof Expr\UnaryMinus;
34+
}
35+
36+
public function analyseExpr(Stmt $stmt, Expr $expr, GeneratorScope $scope, ExpressionContext $context, ?callable $alternativeNodeCallback): Generator
37+
{
38+
$result = yield new ExprAnalysisRequest($stmt, $expr->expr, $scope, $context->enterDeep(), $alternativeNodeCallback);
39+
40+
$type = $this->initializerExprTypeResolver->getUnaryMinusTypeFromType($expr->expr, $result->type);
41+
$nativeType = $this->initializerExprTypeResolver->getUnaryMinusTypeFromType($expr->expr, $result->nativeType);
42+
if ($type instanceof IntegerRangeType) {
43+
$mulResult = yield new ExprAnalysisRequest($stmt, new Expr\BinaryOp\Mul($expr, new Int_(-1)), $scope, $context->enterDeep(), new NoopNodeCallback());
44+
$type = $mulResult->type;
45+
$nativeType = $mulResult->nativeType;
46+
}
47+
48+
return new ExprAnalysisResult(
49+
$type,
50+
$nativeType,
51+
$result->scope,
52+
hasYield: $result->hasYield,
53+
isAlwaysTerminating: $result->isAlwaysTerminating,
54+
throwPoints: $result->throwPoints,
55+
impurePoints: $result->impurePoints,
56+
specifiedTruthyTypes: new SpecifiedTypes(),
57+
specifiedFalseyTypes: new SpecifiedTypes(),
58+
specifiedNullTypes: new SpecifiedTypes(),
59+
);
60+
}
61+
62+
}

src/Reflection/InitializerExprTypeResolver.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,7 +2522,19 @@ public function getClassConstFetchType(Name|Expr $class, string $constantName, ?
25222522
*/
25232523
public function getUnaryMinusType(Expr $expr, callable $getTypeCallback): Type
25242524
{
2525-
$type = $getTypeCallback($expr)->toNumber();
2525+
$type = $getTypeCallback($expr);
2526+
2527+
$type = $this->getUnaryMinusTypeFromType($expr, $type);
2528+
if ($type instanceof IntegerRangeType) {
2529+
return $getTypeCallback(new Expr\BinaryOp\Mul($expr, new Int_(-1)));
2530+
}
2531+
2532+
return $type;
2533+
}
2534+
2535+
public function getUnaryMinusTypeFromType(Expr $expr, Type $type): Type
2536+
{
2537+
$type = $type->toNumber();
25262538
$scalarValues = $type->getConstantScalarValues();
25272539

25282540
if (count($scalarValues) > 0) {
@@ -2543,10 +2555,6 @@ public function getUnaryMinusType(Expr $expr, callable $getTypeCallback): Type
25432555
return TypeCombinator::union(...$newTypes);
25442556
}
25452557

2546-
if ($type instanceof IntegerRangeType) {
2547-
return $getTypeCallback(new Expr\BinaryOp\Mul($expr, new Int_(-1)));
2548-
}
2549-
25502558
return $type;
25512559
}
25522560

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,28 @@ public function doConcat($a, $b, string $c, string $d): void
191191
assertNativeType('string', $c . $d);
192192
}
193193

194-
function doUnaryPlus(int $i) {
194+
/**
195+
* @param int $ii
196+
*/
197+
function doUnaryPlus(int $i, $ii)
198+
{
195199
$a = '1';
196200

197201
assertType('1', +$a);
198202
assertNativeType('1', +$a);
199203
assertType('int', +$i);
200204
assertNativeType('int', +$i);
205+
assertType('int', +$ii);
206+
assertNativeType('float|int', +$ii);
207+
}
208+
209+
function doUnaryMinus(int $i) {
210+
$a = '1';
211+
212+
assertType('-1', -$a);
213+
assertNativeType('-1', -$a);
214+
assertType('int', -$i);
215+
assertNativeType('int', -$i);
201216
}
202217

203218
/**

0 commit comments

Comments
 (0)