1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-02 17:52:45 +01:00
psalm/tests/GeneratorTest.php

401 lines
13 KiB
PHP
Raw Normal View History

2020-04-06 14:57:18 +02:00
<?php
2020-04-06 14:57:18 +02:00
namespace Psalm\Tests;
2021-12-04 21:55:53 +01:00
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
2020-04-06 14:57:18 +02:00
class GeneratorTest extends TestCase
{
2021-12-04 21:55:53 +01:00
use InvalidCodeAnalysisTestTrait;
use ValidCodeAnalysisTestTrait;
2020-04-06 14:57:18 +02:00
/**
Add support for strict arrays, fix type alias intersection, fix array_is_list assertion on non-lists (#8395) * Immutable CodeLocation * Remove excess clones * Remove external clones * Remove leftover clones * Fix final clone issue * Immutable storages * Refactoring * Fixes * Fixes * Fix * Fix * Fixes * Simplify * Fixes * Fix * Fixes * Update * Fix * Cache global types * Fix * Update * Update * Fixes * Fixes * Refactor * Fixes * Fix * Fix * More caching * Fix * Fix * Update * Update * Fix * Fixes * Update * Refactor * Update * Fixes * Break one more test * Fix * FIx * Fix * Fix * Fix * Fix * Improve performance and readability * Equivalent logic * Fixes * Revert * Revert "Revert" This reverts commit f9175100c8452c80559234200663fd4c4f4dd889. * Fix * Fix reference bug * Make default TypeVisitor immutable * Bugfix * Remove clones * Partial refactoring * Refactoring * Fixes * Fix * Fixes * Fixes * cs-fix * Fix final bugs * Add test * Misc fixes * Update * Fixes * Experiment with removing different property * revert "Experiment with removing different property" This reverts commit ac1156e077fc4ea633530d51096d27b6e88bfdf9. * Uniform naming * Uniform naming * Hack hotfix * Clean up $_FILES ref #8621 * Undo hack, try fixing properly * Helper method * Remove redundant call * Partially fix bugs * Cleanup * Change defaults * Fix bug * Fix (?, hope this doesn't break anything else) * cs-fix * Review fixes * Bugfix * Bugfix * Improve logic * Add support for list{} and callable-list{} types, properly implement array_is_list assertions (fixes #8389) * Default to sealed arrays * Fix array_merge bug * Fixes * Fix * Sealed type checks * Properly infer properties-of and get_object_vars on final classes * Fix array_map zipping * Fix tests * Fixes * Fixes * Fix more stuff * Recursively resolve type aliases * Fix typo * Fixes * Fix array_is_list assertion on keyed array * Add BC docs * Fixes * fix * Update * Update * Update * Update * Seal arrays with count assertions * Fix #8528 * Fix * Update * Improve sealed array foreach logic * get_object_vars on template properties * Fix sealed array assertion reconciler logic * Improved reconciler * Add tests * Single source of truth for test types * Fix tests * Fixup tests * Fixup tests * Fixup tests * Update * Fix tests * Fix tests * Final fixes * Fixes * Use list syntax only when needed * Fix tests * Cs-fix * Update docs * Update docs * Update docs * Update docs * Update docs * Document missing types * Update docs * Improve class-string-map docs * Update * Update * I love working on psalm :) * Keep arrays unsealed by default * Fixup tests * Fix syntax mistake * cs-fix * Fix typo * Re-import missing types * Keep strict types only in return types * argc/argv fixes * argc/argv fixes * Fix test * Comment-out valinor code, pinging @romm pls merge https://github.com/CuyZ/Valinor/pull/246 so we can add valinor to the psalm docs :)
2022-11-05 22:34:42 +01:00
*
2020-04-06 14:57:18 +02:00
*/
public function providerValidCodeParse(): iterable
2020-04-06 14:57:18 +02:00
{
return [
'generator' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
/**
* @param int $start
* @param int $limit
* @param int $step
* @return Generator<int>
*/
function xrange($start, $limit, $step = 1) {
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
}
$a = null;
/*
* Note that an array is never created or returned,
* which saves memory.
*/
foreach (xrange(1, 9, 2) as $number) {
$a = $number;
}',
'assertions' => [
'$a' => 'int|null',
],
],
'generatorReturnType' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
/** @return Generator<int, stdClass> */
function g():Generator { yield new stdClass; }
$g = g();',
'assertions' => [
'$g' => 'Generator<int, stdClass, mixed, mixed>',
],
],
'generatorSend' => [
'code' => '<?php
/** @return Generator<int, string, DateTimeInterface, void> */
function g(): Generator {
$date = yield 1 => "string";
$date->format("m");
}
g()->send(new \DateTime("now"));
',
],
'generatorSendInvalidArgument' => [
'code' => '<?php
/** @return Generator<int, string, DateTimeInterface, void> */
function g(): Generator {
yield 1 => "string";
}
g()->send(1);
',
'assertions' => [],
'ignored_issues' => ['InvalidArgument'],
],
2020-04-06 14:57:18 +02:00
'generatorDelegation' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
/**
* @return Generator<int, int, mixed, int>
*/
function count_to_ten(): Generator {
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from seven_eight();
return yield from nine_ten();
}
/**
* @return Generator<int, int>
*/
function seven_eight(): Generator {
yield 7;
yield from eight();
}
/**
* @return Generator<int,int>
*/
function eight(): Generator {
yield 8;
}
/**
* @return Generator<int,int, mixed, int>
*/
function nine_ten(): Generator {
yield 9;
return 10;
}
$gen = count_to_ten();
foreach ($gen as $num) {
echo "$num ";
}
$gen2 = $gen->getReturn();',
'assertions' => [
'$gen' => 'Generator<int, int, mixed, int>',
'$gen2' => 'int',
],
'ignored_issues' => ['MixedAssignment'],
2020-04-06 14:57:18 +02:00
],
'yieldFromArray' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
/**
* @return Generator<int, int, mixed, void>
*/
function Bar() : Generator {
yield from [1];
}',
],
'generatorWithNestedYield' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
function other_generator(): Generator {
yield "traffic";
return 1;
}
function foo(): Generator {
/** @var int */
$value = yield from other_generator();
var_export($value);
}',
],
'generatorVoidReturn' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
/**
* @return Generator
*/
function generator2() : Generator {
if (rand(0,1)) {
return;
}
yield 2;
}',
],
'returnType' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
function takesInt(int $i) : void {
echo $i;
}
function takesString(string $s) : void {
echo $s;
}
/**
* @return Generator<int, string, mixed, int>
*/
function other_generator() : Generator {
yield "traffic";
return 1;
}
/**
* @return Generator<int, string>
*/
function foo() : Generator {
$a = yield from other_generator();
takesInt($a);
}
foreach (foo() as $s) {
takesString($s);
}',
],
'expectNonNullableTypeWithYield' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
function example() : Generator {
yield from [2];
return null;
}',
],
'yieldFromIterable' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
/**
* @param iterable<int, string> $s
* @return Generator<int, string>
*/
function foo(iterable $s) : Traversable {
yield from $s;
}',
],
'yieldWithReturn' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
/** @return Generator<int, string, int, int> */
function gen(): Generator {
yield 3 => "abc";
$foo = 4;
return $foo;
}'
],
'echoYield' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
/** @return Generator<void, void, string, void> */
function gen(): Generator {
echo yield;
}'
],
'yieldFromTwiceWithVoidSend' => [
'code' => '<?php
/**
* @return \Generator<int, string, void, string>
*/
function test(): \Generator {
return yield "value";
}
function load(string $rsa_key): \Generator {
echo (yield from test()) . (yield from test());
return 5;
}'
],
'iteratorUnion' => [
'code' => '<?php
/** @return Iterator|IteratorAggregate */
function getIteratorOrAggregate() {
yield 2;
}
echo json_encode(iterator_to_array(getIteratorOrAggregate()));'
],
'yieldNonExistentClass' => [
'code' => '<?php
class T {
private const FACTORIES = [
ClassNotExisting::class,
];
function f() : Generator {
foreach (self::FACTORIES as $f) {
if (class_exists($f)) {
yield new $f();
}
}
}
}',
'assertions' => [],
'ignored_issues' => ['UndefinedClass']
],
'fillTemplatesForIteratorFromGenerator' => [
'code' => '<?php
/**
* @return Generator<int, string>
*/
function generator(): Generator
{
yield "test";
}
$iterator = new NoRewindIterator(generator());
',
'assertions' => [
'$iterator' => 'NoRewindIterator<int, string, Generator<int, string, mixed, mixed>>',
]
],
2021-11-01 19:04:49 +01:00
'detectYieldInNew' => [
'code' => '<?php
2021-11-01 19:04:37 +01:00
/** @psalm-suppress MissingClosureReturnType */
$_a = function() { return new RuntimeException(yield "a"); };
',
'assertions' => [
'$_a' => 'pure-Closure():Generator<int, string, mixed, RuntimeException>',
2021-11-01 19:04:37 +01:00
]
],
2021-12-05 13:33:18 +01:00
'detectYieldInArray' => [
'code' => '<?php
2021-12-05 13:33:18 +01:00
/** @psalm-suppress MissingClosureReturnType */
$_a = function() { return [yield "a"]; };
',
'assertions' => [
'$_a' => 'pure-Closure():Generator<int, string, mixed, list{string}>',
2021-12-05 13:33:18 +01:00
]
],
2020-04-06 14:57:18 +02:00
];
}
/**
Add support for strict arrays, fix type alias intersection, fix array_is_list assertion on non-lists (#8395) * Immutable CodeLocation * Remove excess clones * Remove external clones * Remove leftover clones * Fix final clone issue * Immutable storages * Refactoring * Fixes * Fixes * Fix * Fix * Fixes * Simplify * Fixes * Fix * Fixes * Update * Fix * Cache global types * Fix * Update * Update * Fixes * Fixes * Refactor * Fixes * Fix * Fix * More caching * Fix * Fix * Update * Update * Fix * Fixes * Update * Refactor * Update * Fixes * Break one more test * Fix * FIx * Fix * Fix * Fix * Fix * Improve performance and readability * Equivalent logic * Fixes * Revert * Revert "Revert" This reverts commit f9175100c8452c80559234200663fd4c4f4dd889. * Fix * Fix reference bug * Make default TypeVisitor immutable * Bugfix * Remove clones * Partial refactoring * Refactoring * Fixes * Fix * Fixes * Fixes * cs-fix * Fix final bugs * Add test * Misc fixes * Update * Fixes * Experiment with removing different property * revert "Experiment with removing different property" This reverts commit ac1156e077fc4ea633530d51096d27b6e88bfdf9. * Uniform naming * Uniform naming * Hack hotfix * Clean up $_FILES ref #8621 * Undo hack, try fixing properly * Helper method * Remove redundant call * Partially fix bugs * Cleanup * Change defaults * Fix bug * Fix (?, hope this doesn't break anything else) * cs-fix * Review fixes * Bugfix * Bugfix * Improve logic * Add support for list{} and callable-list{} types, properly implement array_is_list assertions (fixes #8389) * Default to sealed arrays * Fix array_merge bug * Fixes * Fix * Sealed type checks * Properly infer properties-of and get_object_vars on final classes * Fix array_map zipping * Fix tests * Fixes * Fixes * Fix more stuff * Recursively resolve type aliases * Fix typo * Fixes * Fix array_is_list assertion on keyed array * Add BC docs * Fixes * fix * Update * Update * Update * Update * Seal arrays with count assertions * Fix #8528 * Fix * Update * Improve sealed array foreach logic * get_object_vars on template properties * Fix sealed array assertion reconciler logic * Improved reconciler * Add tests * Single source of truth for test types * Fix tests * Fixup tests * Fixup tests * Fixup tests * Update * Fix tests * Fix tests * Final fixes * Fixes * Use list syntax only when needed * Fix tests * Cs-fix * Update docs * Update docs * Update docs * Update docs * Update docs * Document missing types * Update docs * Improve class-string-map docs * Update * Update * I love working on psalm :) * Keep arrays unsealed by default * Fixup tests * Fix syntax mistake * cs-fix * Fix typo * Re-import missing types * Keep strict types only in return types * argc/argv fixes * argc/argv fixes * Fix test * Comment-out valinor code, pinging @romm pls merge https://github.com/CuyZ/Valinor/pull/246 so we can add valinor to the psalm docs :)
2022-11-05 22:34:42 +01:00
*
2020-04-06 14:57:18 +02:00
*/
public function providerInvalidCodeParse(): iterable
2020-04-06 14:57:18 +02:00
{
return [
'shouldWarnAboutNoGeneratorReturn' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
function generator2() : Generator {
if (rand(0,1)) {
return;
}
yield 2;
}
/**
* @psalm-suppress InvalidNullableReturnType
*/
function notagenerator() : Generator {
if (rand(0, 1)) {
return;
}
return generator2();
}',
'error_message' => 'InvalidReturnStatement',
],
'expectNonNullableTypeWithNullReturn' => [
'code' => '<?php
2020-04-06 14:57:18 +02:00
function example() : Generator {
yield from [2];
return null;
}
function example2() : Generator {
if (rand(0, 1)) {
return example();
}
return null;
}',
'error_message' => 'NullableReturnStatement',
],
'invalidIterator' => [
'code' => '<?php
function example() : int {
return 0;
}
function example2() : Generator {
yield from example();
}',
'error_message' => 'InvalidIterator',
],
'rawObjectIteration' => [
'code' => '<?php
class A {
/** @var ?string */
public $foo;
}
function example() : Generator {
$arr = new A;
yield from $arr;
}',
'error_message' => 'RawObjectIteration',
],
'possibleRawObjectIteration' => [
'code' => '<?php
class A {
/** @var ?string */
public $foo;
}
class B extends A {}
function bar(A $a): void {}
function gen() : Generator {
$arr = [];
if (rand(0, 10) > 5) {
$arr[] = new A;
} else {
$arr = new B;
}
yield from $arr;
}',
'error_message' => 'PossibleRawObjectIteration',
],
'possibleRawObjectIterationFromIsset' => [
'code' => '<?php
function foo(array $a) : Generator {
if (isset($a["a"]["b"])) {
yield from $a["a"];
}
}',
'error_message' => 'PossibleRawObjectIteration',
],
2020-04-06 14:57:18 +02:00
];
}
}