Skip to content

Commit ddb4fe8

Browse files
VincentLangletphpstan-bot
authored andcommitted
Fix phpstan/phpstan#11073: Nullsafe operator chaining false positive
- Fixed DateTimeModifyReturnTypeExtension to strip null from callee type before returning it as the method's return type - The extension was returning $scope->getType($methodCall->var) which includes null from the nullsafe operator, causing "Cannot call method on DateTimeImmutable|null" false positive for chained calls like $date?->modify('+1 year')->setTime(23, 59, 59) - Updated existing date-format.php test assertion (null was incorrectly expected in modify() return type) - Added regression tests for the nullsafe chaining scenario
1 parent 7b8a821 commit ddb4fe8

File tree

5 files changed

+46
-2
lines changed

5 files changed

+46
-2
lines changed

src/Type/Php/DateTimeModifyReturnTypeExtension.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
7777

7878
return null;
7979
} elseif ($hasDateTime) {
80-
return $scope->getType($methodCall->var);
80+
$callerType = $scope->getType($methodCall->var);
81+
if (TypeCombinator::containsNull($callerType)) {
82+
$callerType = TypeCombinator::removeNull($callerType);
83+
}
84+
return $callerType;
8185
}
8286

8387
if ($this->phpVersion->hasDateTimeExceptions()) {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug11073Nsrt;
6+
7+
use DateTimeImmutable;
8+
use function PHPStan\Testing\assertType;
9+
10+
class HelloWorld
11+
{
12+
public function sayHello(?DateTimeImmutable $date): void
13+
{
14+
assertType('DateTimeImmutable|null', $date?->modify('+1 year')->setTime(23, 59, 59));
15+
}
16+
}

tests/PHPStan/Analyser/nsrt/date-format.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,5 @@ function (\DateTimeImmutable $dt, string $s): void {
4545
};
4646

4747
function (?\DateTimeImmutable $d): void {
48-
assertType('DateTimeImmutable|null', $d->modify('+1 day'));
48+
assertType('DateTimeImmutable', $d->modify('+1 day'));
4949
};

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3962,4 +3962,13 @@ public function testBug11463(): void
39623962
]);
39633963
}
39643964

3965+
#[RequiresPhp('>= 8.0')]
3966+
public function testBug11073(): void
3967+
{
3968+
$this->checkThisOnly = false;
3969+
$this->checkNullables = true;
3970+
$this->checkUnionTypes = true;
3971+
$this->analyse([__DIR__ . '/data/bug-11073.php'], []);
3972+
}
3973+
39653974
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug11073;
6+
7+
use DateTimeImmutable;
8+
9+
class HelloWorld
10+
{
11+
public function sayHello(?DateTimeImmutable $date): ?DateTimeImmutable
12+
{
13+
return $date?->modify('+1 year')->setTime(23, 59, 59);
14+
}
15+
}

0 commit comments

Comments
 (0)