Skip to content

Commit

Permalink
Add return type provider for date/gmdate
Browse files Browse the repository at this point in the history
  • Loading branch information
robchett committed Apr 21, 2023
1 parent 5efddb4 commit 1a1d9c9
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Psalm\Internal\Provider\ReturnTypeProvider\ArraySliceReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\ArraySpliceReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\BasenameReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\DateReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\DirnameReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\FilterVarReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\FirstArgStringReturnTypeProvider;
Expand Down Expand Up @@ -101,6 +102,7 @@ public function __construct()
$this->registerClass(InArrayReturnTypeProvider::class);
$this->registerClass(RoundReturnTypeProvider::class);
$this->registerClass(MbInternalEncodingReturnTypeProvider::class);
$this->registerClass(DateReturnTypeProvider::class);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace Psalm\Internal\Provider\ReturnTypeProvider;

use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
use Psalm\Type;
use Psalm\Type\Union;

use function array_values;
use function preg_match;

/**
* @internal
*/
class DateReturnTypeProvider implements FunctionReturnTypeProviderInterface
{
/**
* @return array<lowercase-string>
*/
public static function getFunctionIds(): array
{
return ['date', 'gmdate'];
}

public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
{
$source = $event->getStatementsSource();
if (!$source instanceof StatementsAnalyzer) {
return null;
}

$call_args = $event->getCallArgs();

if (isset($call_args[0])) {
$type = $source->node_data->getType($call_args[0]->value);
if ($type !== null && $type->isSingle()) {
$atomic_type = array_values($type->getAtomicTypes())[0];
if ($atomic_type instanceof Type\Atomic\TLiteralString
&& ($format_val = $atomic_type->value)
&& preg_match('/[djNwzWmntLoYyBgGhHisuvZUI]+/', $format_val)
) {
return Type::getNumericString();
}
}
}

if (!isset($call_args[1])) {
return Type::getString();
}

$type = $source->node_data->getType($call_args[1]->value);
if ($type !== null && $type->isSingle()) {
$atomic_type = array_values($type->getAtomicTypes())[0];
if ($atomic_type instanceof Type\Atomic\TNumeric
|| $atomic_type instanceof Type\Atomic\TInt
) {
return Type::getString();
}
}
return Type::combineUnionTypes(Type::getString(), Type::getFalse());
}
}
11 changes: 0 additions & 11 deletions stubs/CoreGenericFunctions.phpstub
Original file line number Diff line number Diff line change
Expand Up @@ -559,17 +559,6 @@ function abs($num) {}
*/
function range($start, $end, $step = 1) {}

/**
* @psalm-pure
*
* @return (
* $format is 'd'|'j'|'N'|'w'|'z'|'W'|'m'|'n'|'t'|'L'|'o'|'Y'|'y'|'B'|'g'|'G'|'h'|'H'|'i'|'s'|'u'|'v'|'Z'|'U'|'I'
* ? numeric-string
* : ($timestamp is numeric ? string : string|false)
* )
*/
function date(string $format, int $timestamp = 0) {}

/**
* @psalm-pure
*
Expand Down
19 changes: 18 additions & 1 deletion tests/FunctionCallTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1731,19 +1731,36 @@ function test() : void {
'dateTest' => [
'code' => '<?php
$y = date("Y");
$ym = date("Ym");
$m = date("m");
$F = date("F");
$y2 = date("Y", 10000);
$F2 = date("F", 10000);
/** @psalm-suppress MixedArgument */
$F3 = date("F", $GLOBALS["F3"]);',
$F3 = date("F", $GLOBALS["F3"]);
$gm_y = gmdate("Y");
$gm_ym = gmdate("Ym");
$gm_m = gmdate("m");
$gm_F = gmdate("F");
$gm_y2 = gmdate("Y", 10000);
$gm_F2 = gmdate("F", 10000);
/** @psalm-suppress MixedArgument */
$gm_F3 = gmdate("F", $GLOBALS["F3"]);',
'assertions' => [
'$y===' => 'numeric-string',
'$ym===' => 'numeric-string',
'$m===' => 'numeric-string',
'$F===' => 'string',
'$y2===' => 'numeric-string',
'$F2===' => 'string',
'$F3===' => 'false|string',
'$gm_y===' => 'numeric-string',
'$gm_ym===' => 'numeric-string',
'$gm_m===' => 'numeric-string',
'$gm_F===' => 'string',
'$gm_y2===' => 'numeric-string',
'$gm_F2===' => 'string',
'$gm_F3===' => 'false|string',
],
],
'sscanfReturnTypeWithTwoParameters' => [
Expand Down

0 comments on commit 1a1d9c9

Please sign in to comment.