From 9d3253482d78a9b6750dd9ff5eb8df858016fdc5 Mon Sep 17 00:00:00 2001 From: Semyon <7ionmail@gmail.com> Date: Fri, 22 Jul 2022 16:03:45 +0300 Subject: [PATCH 1/3] Add stub for DatePeriod --- stubs/CoreImmutableClasses.phpstub | 21 +++++++++++++++++++++ tests/CoreStubsTest.php | 30 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index f9c8d6e3e34..20acef61755 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -16,6 +16,27 @@ class DateTimeZone public function __construct(string $timezone) {} } +/** + * @psalm-immutable + * + * @template-covariant From of string|DateTimeInterface + * @implements IteratorAggregate + */ +class DatePeriod implements IteratorAggregate +{ + const EXCLUDE_START_DATE = 1; + /** + * @param From $from + * @param (From is string ? 0|self::EXCLUDE_START_DATE : DateInterval) $interval_or_options + * @param (From is string ? never : DateTimeInterface|positive-int) $end_or_recurrences + * @param (From is string ? never : 0|self::EXCLUDE_START_DATE) $options + */ + public function __construct($from, $interval_or_options = 0, $end_or_recurrences = 1, $options = 0) {} + + /** @psalm-return (From is string ? (Traversable&Iterator) : (Traversable&Iterator)) */ + public function getIterator(): Iterator {} +} + /** * @psalm-taint-specialize */ diff --git a/tests/CoreStubsTest.php b/tests/CoreStubsTest.php index 8bbe6435522..38a38672c40 100644 --- a/tests/CoreStubsTest.php +++ b/tests/CoreStubsTest.php @@ -33,5 +33,35 @@ public function providerValidCodeParse(): iterable 'error_levels' => [], 'php_version' => '8.0', ]; + yield 'Iterating over \DatePeriod (#5954)' => [ + 'format("Y-m-d"); + }', + 'assertions' => [ + '$period' => 'DatePeriod', + '$dt' => 'DateTimeImmutable|null' + ], + ]; + yield 'Iterating over \DatePeriod (#5954), ISO string' => [ + 'format("Y-m-d"); + }', + 'assertions' => [ + '$period' => 'DatePeriod', + '$dt' => 'DateTime|null' + ], + ]; } } From 462ce7138ac926e832a37ed4be47b1a8ce70e1d2 Mon Sep 17 00:00:00 2001 From: Semyon <7ionmail@gmail.com> Date: Mon, 25 Jul 2022 16:37:49 +0300 Subject: [PATCH 2/3] Make DatePeriod implement Traversable oh PHP 7, rename constructor params --- stubs/CoreImmutableClasses.phpstub | 19 ++++++------- stubs/Php80.phpstub | 21 +++++++++++++++ tests/CoreStubsTest.php | 43 +++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 20acef61755..405f89b43cd 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -19,22 +19,19 @@ class DateTimeZone /** * @psalm-immutable * - * @template-covariant From of string|DateTimeInterface - * @implements IteratorAggregate + * @template-covariant Start of string|DateTimeInterface + * @implements Traversable */ -class DatePeriod implements IteratorAggregate +class DatePeriod implements Traversable { const EXCLUDE_START_DATE = 1; /** - * @param From $from - * @param (From is string ? 0|self::EXCLUDE_START_DATE : DateInterval) $interval_or_options - * @param (From is string ? never : DateTimeInterface|positive-int) $end_or_recurrences - * @param (From is string ? never : 0|self::EXCLUDE_START_DATE) $options + * @param Start $start + * @param (Start is string ? 0|self::EXCLUDE_START_DATE : DateInterval) $interval + * @param (Start is string ? never : DateTimeInterface|positive-int) $end + * @param (Start is string ? never : 0|self::EXCLUDE_START_DATE) $options */ - public function __construct($from, $interval_or_options = 0, $end_or_recurrences = 1, $options = 0) {} - - /** @psalm-return (From is string ? (Traversable&Iterator) : (Traversable&Iterator)) */ - public function getIterator(): Iterator {} + public function __construct($start, $interval = 0, $end = 1, $options = 0) {} } /** diff --git a/stubs/Php80.phpstub b/stubs/Php80.phpstub index 877b156f3a0..97b0621178b 100644 --- a/stubs/Php80.phpstub +++ b/stubs/Php80.phpstub @@ -82,3 +82,24 @@ class ReflectionUnionType extends ReflectionType { } class UnhandledMatchError extends Error {} + +/** + * @psalm-immutable + * + * @template-covariant Start of string|DateTimeInterface + * @implements IteratorAggregate + */ +class DatePeriod implements IteratorAggregate +{ + const EXCLUDE_START_DATE = 1; + /** + * @param Start $start + * @param (Start is string ? 0|self::EXCLUDE_START_DATE : DateInterval) $interval + * @param (Start is string ? never : DateTimeInterface|positive-int) $end + * @param (Start is string ? never : 0|self::EXCLUDE_START_DATE) $options + */ + public function __construct($start, $interval = 0, $end = 1, $options = 0) {} + + /** @psalm-return (Start is string ? (Traversable&Iterator) : (Traversable&Iterator)) */ + public function getIterator(): Iterator {} +} \ No newline at end of file diff --git a/tests/CoreStubsTest.php b/tests/CoreStubsTest.php index 38a38672c40..d6f0a85a916 100644 --- a/tests/CoreStubsTest.php +++ b/tests/CoreStubsTest.php @@ -33,7 +33,26 @@ public function providerValidCodeParse(): iterable 'error_levels' => [], 'php_version' => '8.0', ]; - yield 'Iterating over \DatePeriod (#5954)' => [ + yield 'Iterating over \DatePeriod (#5954) PHP7 Traversable' => [ + 'format("Y-m-d"); + }', + 'assertions' => [ + '$period' => 'DatePeriod', + '$dt' => 'DateTimeInterface|null' + ], + 'error_levels' => [], + 'php_version' => '7.3', + ]; + yield 'Iterating over \DatePeriod (#5954) PHP8 IteratorAggregate' => [ ' 'DatePeriod', '$dt' => 'DateTimeImmutable|null' ], + 'error_levels' => [], + 'php_version' => '8.0', ]; yield 'Iterating over \DatePeriod (#5954), ISO string' => [ ' 'DatePeriod', '$dt' => 'DateTime|null' ], + 'error_levels' => [], + 'php_version' => '8.0', + ]; + yield 'DatePeriod implements only Traversable on PHP 7' => [ + ' [], + 'error_levels' => [], + 'php_version' => '7.3', + ]; + yield 'DatePeriod implements IteratorAggregate on PHP 8' => [ + ' [], + 'error_levels' => ['RedundantCondition'], + 'php_version' => '8.0', ]; } } From b1295d6894f5c8308ad3c846a0e994886352450e Mon Sep 17 00:00:00 2001 From: Semyon <7ionmail@gmail.com> Date: Mon, 25 Jul 2022 17:15:28 +0300 Subject: [PATCH 3/3] Code style --- src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php | 2 -- stubs/Php80.phpstub | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index cd885a0c87b..385a954c946 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -65,8 +65,6 @@ use function array_merge; use function array_search; use function array_values; -use function assert; -use function class_exists; use function count; use function end; use function in_array; diff --git a/stubs/Php80.phpstub b/stubs/Php80.phpstub index 97b0621178b..001ebf17ae6 100644 --- a/stubs/Php80.phpstub +++ b/stubs/Php80.phpstub @@ -102,4 +102,4 @@ class DatePeriod implements IteratorAggregate /** @psalm-return (Start is string ? (Traversable&Iterator) : (Traversable&Iterator)) */ public function getIterator(): Iterator {} -} \ No newline at end of file +}