Skip to content

Commit 911404a

Browse files
github-actions[bot]phpstan-bot
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 2681e50 commit 911404a

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
@@ -428,42 +428,59 @@ public function specifyTypesInCondition(
428428
}
429429
}
430430

431+
$leftExpr = $expr->left;
432+
if ($leftExpr instanceof Expr\Cast) {
433+
$castedType = $scope->getType($leftExpr);
434+
$innerType = $scope->getType($leftExpr->expr);
435+
if ($castedType->equals($innerType)) {
436+
$leftExpr = $leftExpr->expr;
437+
}
438+
}
439+
$rightExpr = $expr->right;
440+
if ($rightExpr instanceof Expr\Cast) {
441+
$castedType = $scope->getType($rightExpr);
442+
$innerType = $scope->getType($rightExpr->expr);
443+
if ($castedType->equals($innerType)) {
444+
$rightExpr = $rightExpr->expr;
445+
}
446+
}
447+
431448
if ($context->true()) {
432-
if (!$expr->left instanceof Node\Scalar) {
449+
if (!$leftExpr instanceof Node\Scalar) {
433450
$result = $result->unionWith(
434451
$this->create(
435-
$expr->left,
452+
$leftExpr,
436453
$orEqual ? $rightType->getSmallerOrEqualType($this->phpVersion) : $rightType->getSmallerType($this->phpVersion),
437454
TypeSpecifierContext::createTruthy(),
438455
$scope,
439456
)->setRootExpr($expr),
440457
);
441458
}
442-
if (!$expr->right instanceof Node\Scalar) {
459+
if (!$rightExpr instanceof Node\Scalar) {
443460
$result = $result->unionWith(
444461
$this->create(
445-
$expr->right,
462+
$rightExpr,
446463
$orEqual ? $leftType->getGreaterOrEqualType($this->phpVersion) : $leftType->getGreaterType($this->phpVersion),
447464
TypeSpecifierContext::createTruthy(),
448465
$scope,
449466
)->setRootExpr($expr),
450467
);
451468
}
452469
} elseif ($context->false()) {
453-
if (!$expr->left instanceof Node\Scalar) {
470+
if (!$leftExpr instanceof Node\Scalar) {
454471
$result = $result->unionWith(
455472
$this->create(
456-
$expr->left,
473+
$leftExpr,
457474
$orEqual ? $rightType->getGreaterType($this->phpVersion) : $rightType->getGreaterOrEqualType($this->phpVersion),
458475
TypeSpecifierContext::createTruthy(),
459476
$scope,
460477
)->setRootExpr($expr),
461478
);
462479
}
463-
if (!$expr->right instanceof Node\Scalar) {
480+
if (!$rightExpr instanceof Node\Scalar) {
464481
$result = $result->unionWith(
465482
$this->create(
466-
$expr->right,
483+
$rightExpr,
467484
$orEqual ? $leftType->getSmallerType($this->phpVersion) : $leftType->getSmallerOrEqualType($this->phpVersion),
468485
TypeSpecifierContext::createTruthy(),
469486
$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)