From 1a1d9c9bf5b48f5e71bdc0a1f9dd19ac20052ea8 Mon Sep 17 00:00:00 2001 From: RobChett Date: Thu, 20 Apr 2023 20:48:12 +0100 Subject: [PATCH] Add return type provider for date/gmdate --- .../Provider/FunctionReturnTypeProvider.php | 2 + .../DateReturnTypeProvider.php | 66 +++++++++++++++++++ stubs/CoreGenericFunctions.phpstub | 11 ---- tests/FunctionCallTest.php | 19 +++++- 4 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 src/Psalm/Internal/Provider/ReturnTypeProvider/DateReturnTypeProvider.php diff --git a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php index 0d2552d8e48..ad1827b52c7 100644 --- a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php @@ -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; @@ -101,6 +102,7 @@ public function __construct() $this->registerClass(InArrayReturnTypeProvider::class); $this->registerClass(RoundReturnTypeProvider::class); $this->registerClass(MbInternalEncodingReturnTypeProvider::class); + $this->registerClass(DateReturnTypeProvider::class); } /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/DateReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/DateReturnTypeProvider.php new file mode 100644 index 00000000000..7fbfedf22d9 --- /dev/null +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/DateReturnTypeProvider.php @@ -0,0 +1,66 @@ + + */ + 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()); + } +} diff --git a/stubs/CoreGenericFunctions.phpstub b/stubs/CoreGenericFunctions.phpstub index 64af461e130..a03447b1f4b 100644 --- a/stubs/CoreGenericFunctions.phpstub +++ b/stubs/CoreGenericFunctions.phpstub @@ -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 * diff --git a/tests/FunctionCallTest.php b/tests/FunctionCallTest.php index 96c960031c7..12d23a8b4c6 100644 --- a/tests/FunctionCallTest.php +++ b/tests/FunctionCallTest.php @@ -1731,19 +1731,36 @@ function test() : void { 'dateTest' => [ 'code' => ' [ '$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' => [