Skip to content

Commit

Permalink
Regression tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Dec 11, 2023
1 parent ff2fa81 commit a75cadd
Show file tree
Hide file tree
Showing 23 changed files with 807 additions and 0 deletions.
8 changes: 8 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3997.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4016.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8127.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7944.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6196.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7301.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9472.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9084.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9764.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10092.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/promoted-properties-types.php');

Expand Down
43 changes: 43 additions & 0 deletions tests/PHPStan/Analyser/data/bug-10092.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Bug10092;

use function PHPStan\Testing\assertType;

/** @template-covariant T */
interface TypeInterface {}

/** @return TypeInterface<int> */
function int() { }

/** @return TypeInterface<0> */
function zero() { }

/** @return TypeInterface<int<1, max>> */
function positive_int() { }

/** @return TypeInterface<numeric-string> */
function numeric_string() { }


/**
* @template T
*
* @param TypeInterface<T> $first
* @param TypeInterface<T> $second
* @param TypeInterface<T> ...$rest
*
* @return TypeInterface<T>
*/
function union(
TypeInterface $first,
TypeInterface $second,
TypeInterface ...$rest
) {

}

function (): void {
assertType('Bug10092\TypeInterface<int|numeric-string>', union(int(), numeric_string()));
assertType('Bug10092\TypeInterface<int<0, max>>', union(positive_int(), zero()));
};
27 changes: 27 additions & 0 deletions tests/PHPStan/Analyser/data/bug-6196.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Bug6196;

use function PHPStan\Testing\assertType;

final class ErrorToExceptionHandler{
private function __construct(){

}

/**
* @phpstan-template TReturn
* @phpstan-param \Closure() : TReturn $closure
*
* @phpstan-return TReturn
* @throws \ErrorException
*/
public static function trap(\Closure $closure){
return $closure();
}
}

function (): void {
assertType('string|false', zlib_decode("aaaaaaa"));
assertType('string|false', ErrorToExceptionHandler::trap(fn() => zlib_decode("aaaaaaa")));
};
29 changes: 29 additions & 0 deletions tests/PHPStan/Analyser/data/bug-7301.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Bug7301;

use Closure;
use function PHPStan\Testing\assertType;

/**
* @template TReturn
* @param Closure(): TReturn $closure
* @return TReturn
*/
function templated($closure)
{
return $closure();
}

function (): void {
/**
* @var Closure(): array<non-empty-string, mixed>
*/
$arg = function () {
return ['key' => 'value'];
};

$result = templated($arg);

assertType('array<non-empty-string, mixed>', $result);
};
31 changes: 31 additions & 0 deletions tests/PHPStan/Analyser/data/bug-7944.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Bug7944;

use function PHPStan\Testing\assertType;

/**
* @template TValue
*/
final class Value
{
/** @var TValue */
public readonly mixed $value;

/**
* @param TValue $value
*/
public function __construct(mixed $value)
{
$this->value = $value;
}
}

/**
* @param non-empty-string $p
*/
function test($p): void {
$value = new Value($p);
assertType('Bug7944\\Value<non-empty-string>', $value);
};

52 changes: 52 additions & 0 deletions tests/PHPStan/Analyser/data/bug-8127.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Bug8127;

use PhpParser\Node\Expr\CallLike;
use PHPStan\Analyser\Scope;
use PHPStan\Collectors\Collector;
use PHPStan\Node\CollectedDataNode;
use PHPStan\Rules\Rule;
use function PHPStan\Testing\assertType;

/**
* @implements Collector<CallLike, array{string, TaintType::TYPE_*, string, int}>
*/
final class SinkCollector implements Collector
{
public function getNodeType(): string
{
return CallLike::class;
}

public function processNode(\PhpParser\Node $node, Scope $scope)
{}
}

class TaintType
{
public const TYPE_INPUT = 'input';
public const TYPE_SQL = 'sql';
public const TYPE_HTML = 'html';

public const TYPES = [self::TYPE_INPUT, self::TYPE_SQL, self::TYPE_HTML];
}

/**
* @implements Rule<CollectedDataNode>
*/
final class TaintRule implements Rule
{
public function getNodeType(): string
{
return CollectedDataNode::class;
}

public function processNode(\PhpParser\Node $node, Scope $scope): array
{
$sinkCollectorData = $node->get(SinkCollector::class);
assertType("array<string, list<array{string, 'html'|'input'|'sql', string, int}>>", $sinkCollectorData);

return [];
}
}
76 changes: 76 additions & 0 deletions tests/PHPStan/Analyser/data/bug-9084.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php // lint >= 8.1

namespace Bug9084;

use function PHPStan\Testing\assertType;

enum UnitType
{
case Mass;

case Length;
}

/**
* @template TUnitType of UnitType::*
*/
interface UnitInterface
{
public function getValue(): float;
}

/**
* @implements UnitInterface<UnitType::Mass>
*/
enum MassUnit: int implements UnitInterface
{
case KiloGram = 1000000;

case Gram = 1000;

case MilliGram = 1;

public function getValue(): float
{
return $this->value;
}
}

/**
* @template TUnit of UnitType::*
*/
class Value
{
public function __construct(
public readonly float $value,
/** @var UnitInterface<TUnit> */
public readonly UnitInterface $unit
) {
}

/**
* @param UnitInterface<TUnit> $unit
* @return Value<TUnit>
*/
public function convert(UnitInterface $unit): Value
{
return new Value($this->value / $unit->getValue(), $unit);
}
}

/**
* @template S
* @param S $value
* @return S
*/
function duplicate($value)
{
return clone $value;
}

function (): void {
$a = new Value(10, MassUnit::KiloGram);
assertType('Bug9084\Value<Bug9084\UnitType::Mass>', $a);
$b = duplicate($a);
assertType('Bug9084\Value<Bug9084\UnitType::Mass>', $b);
};
74 changes: 74 additions & 0 deletions tests/PHPStan/Analyser/data/bug-9472.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace Bug9472;

use Closure;
use function PHPStan\Testing\assertType;

/**
* @template Tk
* @template Tv
* @template T
* @param array<Tk, Tv> $iterable
* @param (Closure(Tv): T) $function
*
* @return list<T>
*/
function map(array $iterable, Closure $function): array
{
$result = [];
foreach ($iterable as $value) {
$result[] = $function($value);
}

return $result;
}

function (): void {
/** @var list<non-empty-string> */
$nonEmptyStrings = [];

map($nonEmptyStrings, static function (string $variable) {
assertType('non-empty-string', $variable);
return $variable;
});
};

/**
* @template Type
* @param Type $x
* @return Type
*/
function identity($x) {
return $x;
}

function (): void {
$x = rand() > 5 ? 'a' : 'b';
assertType('\'a\'|\'b\'', $x);
$y = identity($x);
assertType('\'a\'|\'b\'', $y);
};

/**
* @template ParseResultType
* @param callable():ParseResultType $parseFunction
* @return ParseResultType|null
*/
function tryParse(callable $parseFunction) {
try {
return $parseFunction();
} catch (\Exception $e) {
return null;
}
}

/** @return array{type: 'typeA'|'typeB'} */
function parseData(mixed $data): array {
return ['type' => 'typeA'];
}

function (): void {
$data = tryParse(fn() => parseData('whatever'));
assertType('array{type: \'typeA\'|\'typeB\'}|null', $data);
};
25 changes: 25 additions & 0 deletions tests/PHPStan/Analyser/data/bug-9764.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Bug9764;

use function PHPStan\Testing\assertType;

/**
* @template T
* @param callable(): T $fnc
* @return T
*/
function result(callable $fnc): mixed
{
return $fnc();
}

function (): void {
/** @var array<non-empty-string, string> $a */
$a = [];
$c = static fn (): array => $a;
assertType('Closure(): array<non-empty-string, string>', $c);

$r = result($c);
assertType('array<non-empty-string, string>', $r);
};
Original file line number Diff line number Diff line change
Expand Up @@ -732,4 +732,19 @@ public function testBug9991(): void
]);
}

public function testBug8166(): void
{
$this->checkExplicitMixed = true;
$this->analyse([__DIR__ . '/data/bug-8166.php'], [
[
'Offset \'b\' does not exist on array{a: 1}.',
22,
],
[
'Offset \'b\' does not exist on array<\'a\', string>.',
23,
],
]);
}

}

0 comments on commit a75cadd

Please sign in to comment.