Skip to content

Commit ce34267

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 b72f6ee commit ce34267

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
@@ -492,42 +492,59 @@ public function specifyTypesInCondition(
492492
}
493493
}
494494

495+
$leftExpr = $expr->left;
496+
if ($leftExpr instanceof Expr\Cast) {
497+
$castedType = $scope->getType($leftExpr);
498+
$innerType = $scope->getType($leftExpr->expr);
499+
if ($castedType->equals($innerType)) {
500+
$leftExpr = $leftExpr->expr;
501+
}
502+
}
503+
$rightExpr = $expr->right;
504+
if ($rightExpr instanceof Expr\Cast) {
505+
$castedType = $scope->getType($rightExpr);
506+
$innerType = $scope->getType($rightExpr->expr);
507+
if ($castedType->equals($innerType)) {
508+
$rightExpr = $rightExpr->expr;
509+
}
510+
}
511+
495512
if ($context->true()) {
496-
if (!$expr->left instanceof Node\Scalar) {
513+
if (!$leftExpr instanceof Node\Scalar) {
497514
$result = $result->unionWith(
498515
$this->create(
499-
$expr->left,
516+
$leftExpr,
500517
$orEqual ? $rightType->getSmallerOrEqualType($this->phpVersion) : $rightType->getSmallerType($this->phpVersion),
501518
TypeSpecifierContext::createTruthy(),
502519
$scope,
503520
)->setRootExpr($expr),
504521
);
505522
}
506-
if (!$expr->right instanceof Node\Scalar) {
523+
if (!$rightExpr instanceof Node\Scalar) {
507524
$result = $result->unionWith(
508525
$this->create(
509-
$expr->right,
526+
$rightExpr,
510527
$orEqual ? $leftType->getGreaterOrEqualType($this->phpVersion) : $leftType->getGreaterType($this->phpVersion),
511528
TypeSpecifierContext::createTruthy(),
512529
$scope,
513530
)->setRootExpr($expr),
514531
);
515532
}
516533
} elseif ($context->false()) {
517-
if (!$expr->left instanceof Node\Scalar) {
534+
if (!$leftExpr instanceof Node\Scalar) {
518535
$result = $result->unionWith(
519536
$this->create(
520-
$expr->left,
537+
$leftExpr,
521538
$orEqual ? $rightType->getGreaterType($this->phpVersion) : $rightType->getGreaterOrEqualType($this->phpVersion),
522539
TypeSpecifierContext::createTruthy(),
523540
$scope,
524541
)->setRootExpr($expr),
525542
);
526543
}
527-
if (!$expr->right instanceof Node\Scalar) {
544+
if (!$rightExpr instanceof Node\Scalar) {
528545
$result = $result->unionWith(
529546
$this->create(
530-
$expr->right,
547+
$rightExpr,
531548
$orEqual ? $leftType->getSmallerType($this->phpVersion) : $leftType->getSmallerOrEqualType($this->phpVersion),
532549
TypeSpecifierContext::createTruthy(),
533550
$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)