Skip to content

Commit

Permalink
Add DateTimeSubMethodThrowTypeExtension
Browse files Browse the repository at this point in the history
  • Loading branch information
VincentLanglet authored Sep 3, 2024
1 parent 1e44c9e commit 9c4bee9
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 0 deletions.
5 changes: 5 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,11 @@ services:
tags:
- phpstan.dynamicMethodThrowTypeExtension

-
class: PHPStan\Type\Php\DateTimeSubMethodThrowTypeExtension
tags:
- phpstan.dynamicMethodThrowTypeExtension

-
class: PHPStan\Type\Php\DateTimeZoneConstructorThrowTypeExtension
tags:
Expand Down
43 changes: 43 additions & 0 deletions src/Type/Php/DateTimeSubMethodThrowTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Php;

use DateTime;
use DateTimeImmutable;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicMethodThrowTypeExtension;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use function count;
use function in_array;

final class DateTimeSubMethodThrowTypeExtension implements DynamicMethodThrowTypeExtension
{

public function __construct(private PhpVersion $phpVersion)
{
}

public function isMethodSupported(MethodReflection $methodReflection): bool
{
return $methodReflection->getName() === 'sub'
&& in_array($methodReflection->getDeclaringClass()->getName(), [DateTime::class, DateTimeImmutable::class], true);
}

public function getThrowTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
{
if (count($methodCall->getArgs()) === 0) {
return null;
}

if (!$this->phpVersion->hasDateTimeExceptions()) {
return null;
}

return new ObjectType('DateInvalidOperationException');
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Testing\RuleTestCase;
use function array_merge;
use const PHP_VERSION_ID;

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

public function testBug11503(): void
{
$errors = [
['Call to method DateTimeImmutable::add() on a separate line has no effect.', 10],
['Call to method DateTimeImmutable::modify() on a separate line has no effect.', 11],
['Call to method DateTimeImmutable::setDate() on a separate line has no effect.', 12],
['Call to method DateTimeImmutable::setISODate() on a separate line has no effect.', 13],
['Call to method DateTimeImmutable::setTime() on a separate line has no effect.', 14],
['Call to method DateTimeImmutable::setTimestamp() on a separate line has no effect.', 15],
['Call to method DateTimeImmutable::setTimezone() on a separate line has no effect.', 17],
];
if (PHP_VERSION_ID < 80300) {
$errors = array_merge([
['Call to method DateTimeImmutable::sub() on a separate line has no effect.', 9],
], $errors);
}

$this->analyse([__DIR__ . '/data/bug-11503.php'], $errors);
}

public function testFirstClassCallables(): void
{
$this->analyse([__DIR__ . '/data/first-class-callable-method-without-side-effect.php'], [
Expand Down
19 changes: 19 additions & 0 deletions tests/PHPStan/Rules/Methods/data/bug-11503.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Bug11503;

class Foo {
public function test() {
$date = new \DateTimeImmutable();
$interval = new \DateInterval('P1M');
$date->sub($interval);
$date->add($interval);
$date->modify('+1 day');
$date->setDate(2024, 8, 13);
$date->setISODate(2024, 1);
$date->setTime(0, 0, 0, 0);
$date->setTimestamp(1);
$zone = new \DateTimeZone('UTC');
$date->setTimezone($zone);
}
}

0 comments on commit 9c4bee9

Please sign in to comment.