Skip to content

Commit 9c4bee9

Browse files
Add DateTimeSubMethodThrowTypeExtension
1 parent 1e44c9e commit 9c4bee9

File tree

4 files changed

+89
-0
lines changed

4 files changed

+89
-0
lines changed

conf/config.neon

+5
Original file line numberDiff line numberDiff line change
@@ -1421,6 +1421,11 @@ services:
14211421
tags:
14221422
- phpstan.dynamicMethodThrowTypeExtension
14231423

1424+
-
1425+
class: PHPStan\Type\Php\DateTimeSubMethodThrowTypeExtension
1426+
tags:
1427+
- phpstan.dynamicMethodThrowTypeExtension
1428+
14241429
-
14251430
class: PHPStan\Type\Php\DateTimeZoneConstructorThrowTypeExtension
14261431
tags:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use DateTime;
6+
use DateTimeImmutable;
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Php\PhpVersion;
10+
use PHPStan\Reflection\MethodReflection;
11+
use PHPStan\Type\DynamicMethodThrowTypeExtension;
12+
use PHPStan\Type\ObjectType;
13+
use PHPStan\Type\Type;
14+
use function count;
15+
use function in_array;
16+
17+
final class DateTimeSubMethodThrowTypeExtension implements DynamicMethodThrowTypeExtension
18+
{
19+
20+
public function __construct(private PhpVersion $phpVersion)
21+
{
22+
}
23+
24+
public function isMethodSupported(MethodReflection $methodReflection): bool
25+
{
26+
return $methodReflection->getName() === 'sub'
27+
&& in_array($methodReflection->getDeclaringClass()->getName(), [DateTime::class, DateTimeImmutable::class], true);
28+
}
29+
30+
public function getThrowTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
31+
{
32+
if (count($methodCall->getArgs()) === 0) {
33+
return null;
34+
}
35+
36+
if (!$this->phpVersion->hasDateTimeExceptions()) {
37+
return null;
38+
}
39+
40+
return new ObjectType('DateInvalidOperationException');
41+
}
42+
43+
}

tests/PHPStan/Rules/Methods/CallToMethodStatementWithoutSideEffectsRuleTest.php

+22
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use PHPStan\Rules\Rule;
66
use PHPStan\Rules\RuleLevelHelper;
77
use PHPStan\Testing\RuleTestCase;
8+
use function array_merge;
9+
use const PHP_VERSION_ID;
810

911
/**
1012
* @extends RuleTestCase<CallToMethodStatementWithoutSideEffectsRule>
@@ -89,6 +91,26 @@ public function testBug4455(): void
8991
$this->analyse([__DIR__ . '/data/bug-4455.php'], []);
9092
}
9193

94+
public function testBug11503(): void
95+
{
96+
$errors = [
97+
['Call to method DateTimeImmutable::add() on a separate line has no effect.', 10],
98+
['Call to method DateTimeImmutable::modify() on a separate line has no effect.', 11],
99+
['Call to method DateTimeImmutable::setDate() on a separate line has no effect.', 12],
100+
['Call to method DateTimeImmutable::setISODate() on a separate line has no effect.', 13],
101+
['Call to method DateTimeImmutable::setTime() on a separate line has no effect.', 14],
102+
['Call to method DateTimeImmutable::setTimestamp() on a separate line has no effect.', 15],
103+
['Call to method DateTimeImmutable::setTimezone() on a separate line has no effect.', 17],
104+
];
105+
if (PHP_VERSION_ID < 80300) {
106+
$errors = array_merge([
107+
['Call to method DateTimeImmutable::sub() on a separate line has no effect.', 9],
108+
], $errors);
109+
}
110+
111+
$this->analyse([__DIR__ . '/data/bug-11503.php'], $errors);
112+
}
113+
92114
public function testFirstClassCallables(): void
93115
{
94116
$this->analyse([__DIR__ . '/data/first-class-callable-method-without-side-effect.php'], [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Bug11503;
4+
5+
class Foo {
6+
public function test() {
7+
$date = new \DateTimeImmutable();
8+
$interval = new \DateInterval('P1M');
9+
$date->sub($interval);
10+
$date->add($interval);
11+
$date->modify('+1 day');
12+
$date->setDate(2024, 8, 13);
13+
$date->setISODate(2024, 1);
14+
$date->setTime(0, 0, 0, 0);
15+
$date->setTimestamp(1);
16+
$zone = new \DateTimeZone('UTC');
17+
$date->setTimezone($zone);
18+
}
19+
}

0 commit comments

Comments
 (0)