1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Add dateTimeModify return type provider

This commit is contained in:
Vincent Langlet 2022-09-06 11:05:40 +02:00
parent 95bb71f8a2
commit 32aedbac58
4 changed files with 160 additions and 1 deletions

View File

@ -7,6 +7,7 @@ use PhpParser;
use Psalm\CodeLocation;
use Psalm\Context;
use Psalm\Internal\Provider\ReturnTypeProvider\ClosureFromCallableReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\DateTimeModifyReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\DomNodeAppendChild;
use Psalm\Internal\Provider\ReturnTypeProvider\ImagickPixelColorReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\PdoStatementReturnTypeProvider;
@ -58,6 +59,7 @@ class MethodReturnTypeProvider
$this->registerClass(SimpleXmlElementAsXml::class);
$this->registerClass(PdoStatementReturnTypeProvider::class);
$this->registerClass(ClosureFromCallableReturnTypeProvider::class);
$this->registerClass(DateTimeModifyReturnTypeProvider::class);
}
/**

View File

@ -0,0 +1,61 @@
<?php
namespace Psalm\Internal\Provider\ReturnTypeProvider;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
use Psalm\Type;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Union;
class DateTimeModifyReturnTypeProvider implements MethodReturnTypeProviderInterface
{
public static function getClassLikeNames(): array
{
return ['DateTime', 'DateTimeImmutable'];
}
public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
{
$statements_source = $event->getSource();
$call_args = $event->getCallArgs();
$method_name_lowercase = $event->getMethodNameLowercase();
if (
!$statements_source instanceof StatementsAnalyzer
|| $method_name_lowercase !== 'modify'
|| !isset($call_args[0])
) {
return null;
}
$first_arg = $call_args[0]->value;
$first_arg_type = $statements_source->node_data->getType($first_arg);
if (!$first_arg_type) {
return null;
}
$has_date_time = false;
$has_false = false;
foreach ($first_arg_type->getAtomicTypes() as $type_part) {
if (!$type_part instanceof TLiteralString) {
return null;
}
if (@(new \DateTime())->modify($type_part->value) === false) {
$has_false = true;
} else {
$has_date_time = true;
}
}
if ($has_false && !$has_date_time) {
return Type::getFalse();
}
if ($has_date_time && !$has_false) {
return Type::parseString($event->getFqClasslikeName());
}
return null;
}
}

96
tests/DateTimeTest.php Normal file
View File

@ -0,0 +1,96 @@
<?php
namespace Psalm\Tests;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
class DateTimeTest extends TestCase
{
use ValidCodeAnalysisTestTrait;
/**
* @return iterable<string,array{string,assertions?:array<string,string>,error_levels?:string[]}>
*/
public function providerValidCodeParse(): iterable
{
return [
'modify' => [
'<?php
function getString(): string
{
return "";
}
$datetime = new DateTime();
$dateTimeImmutable = new DateTimeImmutable();
$a = $datetime->modify(getString());
$b = $dateTimeImmutable->modify(getString());
',
'assertions' => [
'$a' => 'DateTime|false',
'$b' => 'DateTimeImmutable|false',
],
],
'modifyWithValidConstant' => [
'<?php
/**
* @return "+1 day"|"+2 day"
*/
function getString(): string
{
return "+1 day";
}
$datetime = new DateTime();
$dateTimeImmutable = new DateTimeImmutable();
$a = $datetime->modify(getString());
$b = $dateTimeImmutable->modify(getString());
',
'assertions' => [
'$a' => 'DateTime',
'$b' => 'DateTimeImmutable',
],
],
'modifyWithInvalidConstant' => [
'<?php
/**
* @return "foo"|"bar"
*/
function getString(): string
{
return "foo";
}
$datetime = new DateTime();
$dateTimeImmutable = new DateTimeImmutable();
$a = $datetime->modify(getString());
$b = $dateTimeImmutable->modify(getString());
',
'assertions' => [
'$a' => 'false',
'$b' => 'false',
],
],
'modifyWithBothConstant' => [
'<?php
/**
* @return "+1 day"|"bar"
*/
function getString(): string
{
return "+1 day";
}
$datetime = new DateTime();
$dateTimeImmutable = new DateTimeImmutable();
$a = $datetime->modify(getString());
$b = $dateTimeImmutable->modify(getString());
',
'assertions' => [
'$a' => 'DateTime|false',
'$b' => 'DateTimeImmutable|false',
],
],
];
}
}

View File

@ -272,7 +272,7 @@ class MethodCallTest extends TestCase
$b = (new DateTimeImmutable())->modify("+3 hours");',
'assertions' => [
'$yesterday' => 'MyDate|false',
'$b' => 'DateTimeImmutable|false',
'$b' => 'DateTimeImmutable',
],
],
'magicCall' => [