Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions src/Testing/TypeInferenceTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,25 +278,38 @@ public static function gatherAssertTypes(string $file): array
* @return array<string, mixed[]>
*/
public static function gatherAssertTypesFromDirectory(string $directory): array
{
$asserts = [];
foreach (self::findTestDataFilesFromDirectory($directory) as $path) {
foreach (self::gatherAssertTypes($path) as $key => $assert) {
$asserts[$key] = $assert;
}
}

return $asserts;
}

/**
* @return list<string>
*/
public static function findTestDataFilesFromDirectory(string $directory): array
{
if (!is_dir($directory)) {
self::fail(sprintf('Directory %s does not exist.', $directory));
}

$finder = new Finder();
$finder->followLinks();
$asserts = [];
$files = [];
foreach ($finder->files()->name('*.php')->in($directory) as $fileInfo) {
$path = $fileInfo->getPathname();
if (self::isFileLintSkipped($path)) {
continue;
}
foreach (self::gatherAssertTypes($path) as $key => $assert) {
$asserts[$key] = $assert;
}
$files[] = $path;
}

return $asserts;
return $files;
}

/**
Expand Down
255 changes: 155 additions & 100 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,87 @@
use EnumTypeAssertions\Foo;
use PHPStan\Testing\TypeInferenceTestCase;
use stdClass;
use function array_shift;
use function define;
use function dirname;
use function implode;
use function sprintf;
use function str_starts_with;
use function strlen;
use function substr;
use const PHP_INT_SIZE;
use const PHP_VERSION_ID;

class NodeScopeResolverTest extends TypeInferenceTestCase
{

public function dataFileAsserts(): iterable
/**
* @return iterable<string>
*/
private static function findTestFiles(): iterable
{
yield from $this->gatherAssertTypesFromDirectory(__DIR__ . '/nsrt');
foreach (self::findTestDataFilesFromDirectory(__DIR__ . '/nsrt') as $testFile) {
yield $testFile;
}

if (PHP_VERSION_ID < 80200 && PHP_VERSION_ID >= 80100) {
yield from $this->gatherAssertTypes(__DIR__ . '/data/enum-reflection-php81.php');
yield __DIR__ . '/data/enum-reflection-php81.php';
}

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

if (PHP_VERSION_ID < 80300) {
if (PHP_VERSION_ID >= 80200) {
yield from $this->gatherAssertTypes(__DIR__ . '/data/mb-strlen-php82.php');
yield __DIR__ . '/data/mb-strlen-php82.php';
} elseif (PHP_VERSION_ID >= 80000) {
yield from $this->gatherAssertTypes(__DIR__ . '/data/mb-strlen-php8.php');
yield __DIR__ . '/data/mb-strlen-php8.php';
} elseif (PHP_VERSION_ID < 70300) {
yield from $this->gatherAssertTypes(__DIR__ . '/data/mb-strlen-php72.php');
yield __DIR__ . '/data/mb-strlen-php72.php';
} else {
yield from $this->gatherAssertTypes(__DIR__ . '/data/mb-strlen-php73.php');
yield __DIR__ . '/data/mb-strlen-php73.php';
}
}

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-6856.php');
yield __DIR__ . '/../Rules/Methods/data/bug-6856.php';

if (PHP_VERSION_ID >= 80000) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Reflection/data/unionTypes.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Reflection/data/mixedType.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Reflection/data/staticReturnType.php');
yield __DIR__ . '/../Reflection/data/unionTypes.php';
yield __DIR__ . '/../Reflection/data/mixedType.php';
yield __DIR__ . '/../Reflection/data/staticReturnType.php';
}

if (PHP_INT_SIZE === 8) {
yield from $this->gatherAssertTypes(__DIR__ . '/data/predefined-constants-64bit.php');
yield __DIR__ . '/data/predefined-constants-64bit.php';
} else {
yield from $this->gatherAssertTypes(__DIR__ . '/data/predefined-constants-32bit.php');
yield __DIR__ . '/data/predefined-constants-32bit.php';
}

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-10577.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-10610.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-2550.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Properties/data/bug-3777.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-4552.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/infer-array-key.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Generics/data/bug-3769.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Generics/data/bug-6301.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/PhpDoc/data/bug-4643.php');
yield __DIR__ . '/../Rules/Variables/data/bug-10577.php';
yield __DIR__ . '/../Rules/Variables/data/bug-10610.php';
yield __DIR__ . '/../Rules/Comparison/data/bug-2550.php';
yield __DIR__ . '/../Rules/Properties/data/bug-3777.php';
yield __DIR__ . '/../Rules/Methods/data/bug-4552.php';
yield __DIR__ . '/../Rules/Methods/data/infer-array-key.php';
yield __DIR__ . '/../Rules/Generics/data/bug-3769.php';
yield __DIR__ . '/../Rules/Generics/data/bug-6301.php';
yield __DIR__ . '/../Rules/PhpDoc/data/bug-4643.php';

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

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-5089.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/unable-to-resolve-callback-parameter-type.php');
yield __DIR__ . '/../Rules/Methods/data/bug-5089.php';
yield __DIR__ . '/../Rules/Methods/data/unable-to-resolve-callback-parameter-type.php';

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/varying-acceptor.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-4415.php');
yield __DIR__ . '/../Rules/Functions/data/varying-acceptor.php';
yield __DIR__ . '/../Rules/Methods/data/bug-4415.php';
if (PHP_VERSION_ID >= 70400) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-5372.php');
yield __DIR__ . '/../Rules/Methods/data/bug-5372.php';
}
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-5372_2.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-5562.php');
yield __DIR__ . '/../Rules/Arrays/data/bug-5372_2.php';
yield __DIR__ . '/../Rules/Methods/data/bug-5562.php';

if (PHP_VERSION_ID >= 80100) {
define('TEST_OBJECT_CONSTANT', new stdClass());
Expand All @@ -82,124 +94,167 @@ public function dataFileAsserts(): iterable
define('TEST_FALSE_CONSTANT', false);
define('TEST_ARRAY_CONSTANT', [true, false, null]);
define('TEST_ENUM_CONSTANT', Foo::ONE);
yield from $this->gatherAssertTypes(__DIR__ . '/data/new-in-initializers-runtime.php');
yield __DIR__ . '/data/new-in-initializers-runtime.php';
}

if (PHP_VERSION_ID >= 70400) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-6473.php');
yield __DIR__ . '/../Rules/Comparison/data/bug-6473.php';
}

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/filter-iterator-child-class.php');
yield __DIR__ . '/../Rules/Methods/data/filter-iterator-child-class.php';

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-5749.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-5757.php');
yield __DIR__ . '/../Rules/Methods/data/bug-5749.php';
yield __DIR__ . '/../Rules/Methods/data/bug-5757.php';

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

if (PHP_VERSION_ID >= 80300) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Constants/data/bug-10212.php');
yield __DIR__ . '/../Rules/Constants/data/bug-10212.php';
}

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-3284.php');
yield __DIR__ . '/../Rules/Methods/data/bug-3284.php';

if (PHP_VERSION_ID >= 80300) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/return-type-class-constant.php');
yield __DIR__ . '/../Rules/Methods/data/return-type-class-constant.php';
}

//define('ALREADY_DEFINED_CONSTANT', true);
//yield from $this->gatherAssertTypes(__DIR__ . '/data/already-defined-constant.php');

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/conditional-complex-templates.php');
yield __DIR__ . '/../Rules/Methods/data/conditional-complex-templates.php';

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-7511.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Properties/data/trait-mixin.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/trait-mixin.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-4708.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-7156.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-6364.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-5758.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-3931.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-7417.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-7469.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-3391.php');
yield __DIR__ . '/../Rules/Methods/data/bug-7511.php';
yield __DIR__ . '/../Rules/Properties/data/trait-mixin.php';
yield __DIR__ . '/../Rules/Methods/data/trait-mixin.php';
yield __DIR__ . '/../Rules/Comparison/data/bug-4708.php';
yield __DIR__ . '/../Rules/Functions/data/bug-7156.php';
yield __DIR__ . '/../Rules/Arrays/data/bug-6364.php';
yield __DIR__ . '/../Rules/Arrays/data/bug-5758.php';
yield __DIR__ . '/../Rules/Functions/data/bug-3931.php';
yield __DIR__ . '/../Rules/Variables/data/bug-7417.php';
yield __DIR__ . '/../Rules/Arrays/data/bug-7469.php';
yield __DIR__ . '/../Rules/Variables/data/bug-3391.php';

if (PHP_VERSION_ID >= 70400) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-anonymous-function-method-constant.php');
yield __DIR__ . '/../Rules/Functions/data/bug-anonymous-function-method-constant.php';
}

if (PHP_VERSION_ID >= 80200) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/true-typehint.php');
yield __DIR__ . '/../Rules/Methods/data/true-typehint.php';
}
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-6000.php');
yield __DIR__ . '/../Rules/Arrays/data/bug-6000.php';

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/slevomat-foreach-unset-bug.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/slevomat-foreach-array-key-exists-bug.php');
yield __DIR__ . '/../Rules/Arrays/data/slevomat-foreach-unset-bug.php';
yield __DIR__ . '/../Rules/Arrays/data/slevomat-foreach-array-key-exists-bug.php';

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

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

yield from $this->gatherAssertTypes(__DIR__ . '/../Analyser/data/is-resource-specified.php');

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-7954.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/docblock-assert-equality.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Properties/data/bug-7839.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/bug-5333.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-8174.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-8169.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-8280.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-8277.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-8113.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-8389.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-8467a.php');
yield __DIR__ . '/../Rules/Functions/data/bug-7823.php';
}

yield __DIR__ . '/../Analyser/data/is-resource-specified.php';

yield __DIR__ . '/../Rules/Arrays/data/bug-7954.php';
yield __DIR__ . '/../Rules/Comparison/data/docblock-assert-equality.php';
yield __DIR__ . '/../Rules/Properties/data/bug-7839.php';
yield __DIR__ . '/../Rules/Classes/data/bug-5333.php';
yield __DIR__ . '/../Rules/Methods/data/bug-8174.php';
yield __DIR__ . '/../Rules/Comparison/data/bug-8169.php';
yield __DIR__ . '/../Rules/Functions/data/bug-8280.php';
yield __DIR__ . '/../Rules/Comparison/data/bug-8277.php';
yield __DIR__ . '/../Rules/Variables/data/bug-8113.php';
yield __DIR__ . '/../Rules/Functions/data/bug-8389.php';
yield __DIR__ . '/../Rules/Arrays/data/bug-8467a.php';
if (PHP_VERSION_ID >= 80100) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-8485.php');
yield __DIR__ . '/../Rules/Comparison/data/bug-8485.php';
}

if (PHP_VERSION_ID >= 80100) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-9007.php');
yield __DIR__ . '/../Rules/Comparison/data/bug-9007.php';
}

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/DeadCode/data/bug-8620.php');
yield __DIR__ . '/../Rules/DeadCode/data/bug-8620.php';

if (PHP_VERSION_ID >= 80200) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Constants/data/bug-8957.php');
yield __DIR__ . '/../Rules/Constants/data/bug-8957.php';
}

if (PHP_VERSION_ID >= 80100) {
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-9499.php');
}

yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/PhpDoc/data/bug-8609-function.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-5365.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-6551.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-9403.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-9542.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-9803.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/PhpDoc/data/bug-10594.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/bug-11591.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/bug-11591-method-tag.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/bug-11591-property-tag.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/mixin-trait-use.php');
yield __DIR__ . '/../Rules/Comparison/data/bug-9499.php';
}

yield __DIR__ . '/../Rules/PhpDoc/data/bug-8609-function.php';
yield __DIR__ . '/../Rules/Comparison/data/bug-5365.php';
yield __DIR__ . '/../Rules/Comparison/data/bug-6551.php';
yield __DIR__ . '/../Rules/Variables/data/bug-9403.php';
yield __DIR__ . '/../Rules/Methods/data/bug-9542.php';
yield __DIR__ . '/../Rules/Functions/data/bug-9803.php';
yield __DIR__ . '/../Rules/PhpDoc/data/bug-10594.php';
yield __DIR__ . '/../Rules/Classes/data/bug-11591.php';
yield __DIR__ . '/../Rules/Classes/data/bug-11591-method-tag.php';
yield __DIR__ . '/../Rules/Classes/data/bug-11591-property-tag.php';
yield __DIR__ . '/../Rules/Classes/data/mixin-trait-use.php';
}

/**
* @dataProvider dataFileAsserts
* @param mixed ...$args
* @return iterable<string, array{string}>
*/
public function testFileAsserts(
string $assertType,
string $file,
...$args,
): void
public static function dataFile(): iterable
{
$this->assertFileAsserts($assertType, $file, ...$args);
$base = dirname(__DIR__, 3) . '/';
$baseLength = strlen($base);

foreach (self::findTestFiles() as $file) {
$testName = $file;
if (str_starts_with($file, $base)) {
$testName = substr($file, $baseLength);
}

yield $testName => [$file];
}
}

/**
* @dataProvider dataFile
*/
public function testFile(string $file): void
{
$asserts = $this->gatherAssertTypes($file);
$this->assertNotCount(0, $asserts, sprintf('File %s has no asserts.', $file));
$failures = [];

foreach ($asserts as $args) {
$assertType = array_shift($args);
$file = array_shift($args);

if ($assertType === 'type') {
$expected = $args[0];
$actual = $args[1];

if ($expected !== $actual) {
$failures[] = sprintf("Line %d:\nExpected: %s\nActual: %s\n", $args[2], $expected, $actual);
}
} elseif ($assertType === 'variableCertainty') {
$expectedCertainty = $args[0];
$actualCertainty = $args[1];
$variableName = $args[2];

if ($expectedCertainty->equals($actualCertainty) !== true) {
$failures[] = sprintf("Certainty of variable \$%s on line %d:\nExpected: %s\nActual: %s\n", $variableName, $args[3], $expectedCertainty->describe(), $actualCertainty->describe());
}
}
}

if ($failures === []) {
return;
}

self::fail(sprintf("Failed assertions in %s:\n\n%s", $file, implode("\n", $failures)));
}

public static function getAdditionalConfigFiles(): array
Expand Down
Loading