Skip to content

Commit bf249cc

Browse files
github-actions[bot]staabm
authored andcommitted
Fix redundant type cast dropping type narrowing in comparisons
- Unwrap redundant cast expressions in Smaller/SmallerOrEqual type specifying so that narrowing propagates to the inner variable instead of the cast expr - When a cast produces the same type as its inner expression (e.g. (int) on an int variable), the narrowing is now applied to the original variable - New regression test in tests/PHPStan/Analyser/nsrt/bug-7858.php Closes phpstan/phpstan#7858
1 parent 5037bd5 commit bf249cc

2 files changed

Lines changed: 46 additions & 8 deletions

File tree

src/Analyser/TypeSpecifier.php

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -474,42 +474,59 @@ public function specifyTypesInCondition(
474474
}
475475
}
476476

477+
$leftExpr = $expr->left;
478+
if ($leftExpr instanceof Expr\Cast) {
479+
$castedType = $scope->getType($leftExpr);
480+
$innerType = $scope->getType($leftExpr->expr);
481+
if ($castedType->equals($innerType)) {
482+
$leftExpr = $leftExpr->expr;
483+
}
484+
}
485+
$rightExpr = $expr->right;
486+
if ($rightExpr instanceof Expr\Cast) {
487+
$castedType = $scope->getType($rightExpr);
488+
$innerType = $scope->getType($rightExpr->expr);
489+
if ($castedType->equals($innerType)) {
490+
$rightExpr = $rightExpr->expr;
491+
}
492+
}
493+
477494
if ($context->true()) {
478-
if (!$expr->left instanceof Node\Scalar) {
495+
if (!$leftExpr instanceof Node\Scalar) {
479496
$result = $result->unionWith(
480497
$this->create(
481-
$expr->left,
498+
$leftExpr,
482499
$orEqual ? $rightType->getSmallerOrEqualType($this->phpVersion) : $rightType->getSmallerType($this->phpVersion),
483500
TypeSpecifierContext::createTruthy(),
484501
$scope,
485502
)->setRootExpr($expr),
486503
);
487504
}
488-
if (!$expr->right instanceof Node\Scalar) {
505+
if (!$rightExpr instanceof Node\Scalar) {
489506
$result = $result->unionWith(
490507
$this->create(
491-
$expr->right,
508+
$rightExpr,
492509
$orEqual ? $leftType->getGreaterOrEqualType($this->phpVersion) : $leftType->getGreaterType($this->phpVersion),
493510
TypeSpecifierContext::createTruthy(),
494511
$scope,
495512
)->setRootExpr($expr),
496513
);
497514
}
498515
} elseif ($context->false()) {
499-
if (!$expr->left instanceof Node\Scalar) {
516+
if (!$leftExpr instanceof Node\Scalar) {
500517
$result = $result->unionWith(
501518
$this->create(
502-
$expr->left,
519+
$leftExpr,
503520
$orEqual ? $rightType->getGreaterType($this->phpVersion) : $rightType->getGreaterOrEqualType($this->phpVersion),
504521
TypeSpecifierContext::createTruthy(),
505522
$scope,
506523
)->setRootExpr($expr),
507524
);
508525
}
509-
if (!$expr->right instanceof Node\Scalar) {
526+
if (!$rightExpr instanceof Node\Scalar) {
510527
$result = $result->unionWith(
511528
$this->create(
512-
$expr->right,
529+
$rightExpr,
513530
$orEqual ? $leftType->getSmallerType($this->phpVersion) : $leftType->getSmallerOrEqualType($this->phpVersion),
514531
TypeSpecifierContext::createTruthy(),
515532
$scope,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug7858;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function foo(int $year): void
8+
{
9+
if (!ctype_digit($year) || (int) $year < 2022) {
10+
throw new \RuntimeException();
11+
}
12+
assertType('int<2022, max>', $year);
13+
}
14+
15+
function bar(int $year): void
16+
{
17+
if (!ctype_digit($year) || $year < 2022) {
18+
throw new \RuntimeException();
19+
}
20+
assertType('int<2022, max>', $year);
21+
}

0 commit comments

Comments
 (0)