Skip to content

Commit

Permalink
Regression tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Feb 28, 2022
1 parent 0dd3d0c commit 86fd620
Show file tree
Hide file tree
Showing 33 changed files with 918 additions and 2 deletions.
Expand Up @@ -8,8 +8,10 @@
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\GeneralizePrecision;
use PHPStan\Type\NeverType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\UnionType;
Expand Down Expand Up @@ -52,8 +54,13 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
$nonEmpty = true;
}

$keyType = TypeCombinator::union(...$keyTypes);
if ($keyType instanceof NeverType && !$keyType->isExplicit()) {
return new ConstantArrayType([], []);
}

$arrayType = new ArrayType(
TypeCombinator::union(...$keyTypes),
$keyType,
TypeCombinator::union(...$valueTypes),
);

Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Expand Up @@ -557,6 +557,12 @@ public function testBug6681(): void
$this->assertNoErrors($errors);
}

public function testBug6212(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-6212.php');
$this->assertNoErrors($errors);
}

/**
* @param string[]|null $allAnalysedFiles
* @return Error[]
Expand Down
12 changes: 12 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Expand Up @@ -737,6 +737,18 @@ public function dataFileAsserts(): iterable

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6698.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/date-format.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6070.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6108.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-1516.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6174.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-5749.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5675.php');

if (PHP_VERSION_ID >= 80000) {
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6505.php');
}

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6699.php');
}

/**
Expand Down
37 changes: 37 additions & 0 deletions tests/PHPStan/Analyser/data/bug-1516.php
@@ -0,0 +1,37 @@
<?php

namespace Bug1516;

use function PHPStan\Testing\assertType;

class FlowNodeManager
{
public function _test() : void
{
$out = [];
$a =
[
'foof' => 'barr',
'ftt' => []
];

foreach ($a as $k => $b) {
$str = 'toto';
assertType('\'toto\'|array{}', $out[$k]);

if (is_array($b)) {
// $out[$k] is redefined there before the array_merge
assertType('\'toto\'|array{}', $out[$k]);
$out[$k] = [];
assertType('array{}', $out[$k]);
$out[$k] = array_merge($out[$k], []);
assertType('array{}', $out[$k]);

} else {
// I think phpstan takes this definition as a string and takes no account of the foreach
$out[$k] = $str;
assertType('\'toto\'', $out[$k]);
}
}
}
}
55 changes: 55 additions & 0 deletions tests/PHPStan/Analyser/data/bug-5675.php
@@ -0,0 +1,55 @@
<?php

namespace Bug5675;

use function PHPStan\Testing\assertType;

class Bar {}

/**
* @template T
*/
class Hello
{
/**
* @param (\Closure(Hello<T>): void)|array<array-key, int>|Bar $column
*/
public function foo($column): void
{
// ...
}

public function bar()
{
/** @var Hello<string> */
$a = new Hello;

$a->foo(function (Hello $h) : void {
assertType('Bug5675\Hello<string>', $h);
});
}
}

/**
* @template T
*/
class Hello2
{
/**
* @param (\Closure(static<T>): void)|array<array-key, int>|Bar $column
*/
public function foo($column): void
{
// ...
}

public function bar()
{
/** @var Hello2<string> */
$a = new Hello2;

$a->foo(function (Hello2 $h) : void {
\PHPStan\Testing\assertType('Bug5675\Hello2<string>', $h);
});
}
}
23 changes: 23 additions & 0 deletions tests/PHPStan/Analyser/data/bug-6070.php
@@ -0,0 +1,23 @@
<?php

namespace Bug6070;

use function PHPStan\Testing\assertType;

class HelloWorld
{
/**
* @return non-empty-array
*/
public function getNonEmptyArray(): array {
$nonEmptyArray = [rand()];

for ($i = 0; $i < 2; $i++) {
$nonEmptyArray[] = 1;
}

assertType('non-empty-array<int, int<0, max>>', $nonEmptyArray);

return $nonEmptyArray;
}
}
38 changes: 38 additions & 0 deletions tests/PHPStan/Analyser/data/bug-6108.php
@@ -0,0 +1,38 @@
<?php

namespace Bug6108;

use function PHPStan\Testing\assertType;

class Foo
{


/**
* @return array{
* a: int[],
* b: int[],
* c: bool
* }
*/
function doFoo(): array {
return [
'a' => [1, 2],
'b' => [3, 4, 5],
'c' => true,
];
}

function doBar()
{
$x = $this->doFoo();
$test = ['a' => true, 'b' => false];
foreach ($test as $key => $value) {
if ($value) {
assertType('\'a\'|\'b\'', $key); // could be just 'a'
assertType('array<int>', $x[$key]);
}
}
}

}
21 changes: 21 additions & 0 deletions tests/PHPStan/Analyser/data/bug-6174.php
@@ -0,0 +1,21 @@
<?php

namespace Bug6174;

use function PHPStan\Testing\assertType;

class HelloWorld
{
private const DEFAULT_VALUE = 10;

public function __construct()
{
$tempValue = (int) ($this->returnValue() ?? self::DEFAULT_VALUE);
assertType('-1|int<1, max>', $tempValue === -1 || $tempValue > 0 ? $tempValue : self::DEFAULT_VALUE);
}

public function returnValue(): ?string
{
return null;
}
}
13 changes: 13 additions & 0 deletions tests/PHPStan/Analyser/data/bug-6212.php
@@ -0,0 +1,13 @@
<?php

namespace Bug6212;

class HelloWorld
{
public function sayHello(\DateTimeImmutable $date): void
{
for ($i = 1; $i <= 1e18; $i *= 10) {

}
}
}
139 changes: 139 additions & 0 deletions tests/PHPStan/Analyser/data/bug-6505.php
@@ -0,0 +1,139 @@
<?php // lint >= 8.0

namespace Bug6505;

use function PHPStan\Testing\assertType;

/** @template T */
interface Type
{
/**
* @param T $val
* @return T
*/
public function validate($val);
}

/**
* @template T
* @implements Type<class-string<T>>
*/
final class ClassStringType implements Type
{
/** @param class-string<T> $classString */
public function __construct(public string $classString)
{
}

public function validate($val) {
return $val;
}
}

/**
* @implements Type<class-string<\stdClass>>
*/
final class StdClassType implements Type
{
public function validate($val) {
return $val;
}
}


/**
* @template T
* @implements Type<T[]>
*/
final class TypeCollection implements Type
{
/** @param Type<T> $type */
public function __construct(public Type $type)
{
}
public function validate($val) {
return $val;
}
}

class Foo
{

public function doFoo()
{
$c = new TypeCollection(new ClassStringType(\stdClass::class));
assertType('c', $c->validate([\stdClass::class]));
$c2 = new TypeCollection(new StdClassType());
assertType('c', $c2->validate([\stdClass::class]));
}

/**
* @template T
* @param T $t
* @return T
*/
function unbounded($t) {
return $t;
}

/**
* @template T of string
* @param T $t
* @return T
*/
function bounded1($t) {
return $t;
}

/**
* @template T of object|class-string
* @param T $t
* @return T
*/
function bounded2($t) {
return $t;
}

/** @param class-string<\stdClass> $p */
function test($p): void {
assertType('c', $this->unbounded($p));
assertType('c', $this->bounded1($p));
assertType('c', $this->bounded2($p));
}

}

/**
* @template TKey of array-key
* @template TValue
*/
class Collection
{
/**
* @var array<TKey, TValue>
*/
protected array $items;

/**
* Create a new collection.
*
* @param array<TKey, TValue>|null $items
* @return void
*/
public function __construct(?array $items = [])
{
$this->items = $items ?? [];
}
}

class Example
{
/** @var array<string, list<class-string>> */
private array $factories = [];

public function getFactories(): void
{
assertType('c', new Collection($this->factories));
}
}

0 comments on commit 86fd620

Please sign in to comment.