Skip to content

Commit

Permalink
TypeInfereceTest - allow 3rd parties same modern style of type infere…
Browse files Browse the repository at this point in the history
…nce testing as in NodeScopeResolverTest
  • Loading branch information
ondrejmirtes committed Apr 20, 2021
1 parent 93baca0 commit 3af8630
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 98 deletions.
111 changes: 111 additions & 0 deletions src/Testing/TypeInferenceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

namespace PHPStan\Testing;

use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Analyser\DirectScopeFactory;
use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\ScopeContext;
use PHPStan\Broker\AnonymousClassNameHelper;
use PHPStan\Broker\Broker;
Expand All @@ -15,9 +19,11 @@
use PHPStan\PhpDoc\PhpDocNodeResolver;
use PHPStan\PhpDoc\PhpDocStringResolver;
use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider;
use PHPStan\TrinaryLogic;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
use PHPStan\Type\FileTypeMapper;
use PHPStan\Type\VerbosityLevel;

abstract class TypeInferenceTest extends \PHPStan\Testing\TestCase
{
Expand Down Expand Up @@ -97,6 +103,111 @@ public function processFile(
);
}

/**
* @param string $assertType
* @param string $file
* @param mixed ...$args
*/
public function assertFileAsserts(
string $assertType,
string $file,
...$args
): void
{
if ($assertType === 'type') {
$expectedType = $args[0];
$expected = $expectedType->getValue();
$actualType = $args[1];
$actual = $actualType->describe(VerbosityLevel::precise());
$this->assertSame(
$expected,
$actual,
sprintf('Expected type %s, got type %s in %s on line %d.', $expected, $actual, $file, $args[2])
);
} elseif ($assertType === 'variableCertainty') {
$expectedCertainty = $args[0];
$actualCertainty = $args[1];
$variableName = $args[2];
$this->assertTrue(
$expectedCertainty->equals($actualCertainty),
sprintf('Expected %s, actual certainty of variable $%s is %s', $expectedCertainty->describe(), $variableName, $actualCertainty->describe())
);
}
}

/**
* @param string $file
* @return array<string, mixed[]>
*/
public function gatherAssertTypes(string $file): array
{
$asserts = [];
$this->processFile($file, function (Node $node, Scope $scope) use (&$asserts, $file): void {
if (!$node instanceof Node\Expr\FuncCall) {
return;
}

$nameNode = $node->name;
if (!$nameNode instanceof Name) {
return;
}

$functionName = $nameNode->toString();
if ($functionName === 'PHPStan\\Analyser\\assertType') {
$expectedType = $scope->getType($node->args[0]->value);
$actualType = $scope->getType($node->args[1]->value);
$assert = ['type', $file, $expectedType, $actualType, $node->getLine()];
} elseif ($functionName === 'PHPStan\\Analyser\\assertNativeType') {
$expectedType = $scope->getNativeType($node->args[0]->value);
$actualType = $scope->getNativeType($node->args[1]->value);
$assert = ['type', $file, $expectedType, $actualType, $node->getLine()];
} elseif ($functionName === 'PHPStan\\Analyser\\assertVariableCertainty') {
$certainty = $node->args[0]->value;
if (!$certainty instanceof StaticCall) {
$this->fail(sprintf('First argument of %s() must be TrinaryLogic call', $functionName));
}
if (!$certainty->class instanceof Node\Name) {
$this->fail(sprintf('ERROR: Invalid TrinaryLogic call.'));
}

if ($certainty->class->toString() !== 'PHPStan\\TrinaryLogic') {
$this->fail(sprintf('ERROR: Invalid TrinaryLogic call.'));
}

if (!$certainty->name instanceof Node\Identifier) {
$this->fail(sprintf('ERROR: Invalid TrinaryLogic call.'));
}

// @phpstan-ignore-next-line
$expectedertaintyValue = TrinaryLogic::{$certainty->name->toString()}();
$variable = $node->args[1]->value;
if (!$variable instanceof Node\Expr\Variable) {
$this->fail(sprintf('ERROR: Invalid assertVariableCertainty call.'));
}
if (!is_string($variable->name)) {
$this->fail(sprintf('ERROR: Invalid assertVariableCertainty call.'));
}

$actualCertaintyValue = $scope->hasVariableType($variable->name);
$assert = ['variableCertainty', $file, $expectedertaintyValue, $actualCertaintyValue, $variable->name];
} else {
return;
}

if (count($node->args) !== 2) {
$this->fail(sprintf(
'ERROR: Wrong %s() call on line %d.',
$functionName,
$node->getLine()
));
}

$asserts[$file . ':' . $node->getLine()] = $assert;
});

return $asserts;
}

/** @return string[] */
protected function getAdditionalAnalysedFiles(): array
{
Expand Down
99 changes: 1 addition & 98 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@

namespace PHPStan\Analyser;

use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Testing\TypeInferenceTest;
use PHPStan\TrinaryLogic;
use PHPStan\Type\VerbosityLevel;
use const PHP_VERSION_ID;

class NodeScopeResolverTest extends TypeInferenceTest
{
Expand Down Expand Up @@ -399,98 +393,7 @@ public function testFileAsserts(
...$args
): void
{
if ($assertType === 'type') {
$expectedType = $args[0];
$expected = $expectedType->getValue();
$actualType = $args[1];
$actual = $actualType->describe(VerbosityLevel::precise());
$this->assertSame(
$expected,
$actual,
sprintf('Expected type %s, got type %s in %s on line %d.', $expected, $actual, $file, $args[2])
);
} elseif ($assertType === 'variableCertainty') {
$expectedCertainty = $args[0];
$actualCertainty = $args[1];
$variableName = $args[2];
$this->assertTrue(
$expectedCertainty->equals($actualCertainty),
sprintf('Expected %s, actual certainty of variable $%s is %s', $expectedCertainty->describe(), $variableName, $actualCertainty->describe())
);
}
}

/**
* @param string $file
* @return array<string, mixed[]>
*/
private function gatherAssertTypes(string $file): array
{
$asserts = [];
$this->processFile($file, function (Node $node, Scope $scope) use (&$asserts, $file): void {
if (!$node instanceof Node\Expr\FuncCall) {
return;
}

$nameNode = $node->name;
if (!$nameNode instanceof Name) {
return;
}

$functionName = $nameNode->toString();
if ($functionName === 'PHPStan\\Analyser\\assertType') {
$expectedType = $scope->getType($node->args[0]->value);
$actualType = $scope->getType($node->args[1]->value);
$assert = ['type', $file, $expectedType, $actualType, $node->getLine()];
} elseif ($functionName === 'PHPStan\\Analyser\\assertNativeType') {
$expectedType = $scope->getNativeType($node->args[0]->value);
$actualType = $scope->getNativeType($node->args[1]->value);
$assert = ['type', $file, $expectedType, $actualType, $node->getLine()];
} elseif ($functionName === 'PHPStan\\Analyser\\assertVariableCertainty') {
$certainty = $node->args[0]->value;
if (!$certainty instanceof StaticCall) {
$this->fail(sprintf('First argument of %s() must be TrinaryLogic call', $functionName));
}
if (!$certainty->class instanceof Node\Name) {
$this->fail(sprintf('ERROR: Invalid TrinaryLogic call.'));
}

if ($certainty->class->toString() !== 'PHPStan\\TrinaryLogic') {
$this->fail(sprintf('ERROR: Invalid TrinaryLogic call.'));
}

if (!$certainty->name instanceof Node\Identifier) {
$this->fail(sprintf('ERROR: Invalid TrinaryLogic call.'));
}

// @phpstan-ignore-next-line
$expectedertaintyValue = TrinaryLogic::{$certainty->name->toString()}();
$variable = $node->args[1]->value;
if (!$variable instanceof Node\Expr\Variable) {
$this->fail(sprintf('ERROR: Invalid assertVariableCertainty call.'));
}
if (!is_string($variable->name)) {
$this->fail(sprintf('ERROR: Invalid assertVariableCertainty call.'));
}

$actualCertaintyValue = $scope->hasVariableType($variable->name);
$assert = ['variableCertainty', $file, $expectedertaintyValue, $actualCertaintyValue, $variable->name];
} else {
return;
}

if (count($node->args) !== 2) {
$this->fail(sprintf(
'ERROR: Wrong %s() call on line %d.',
$functionName,
$node->getLine()
));
}

$asserts[$file . ':' . $node->getLine()] = $assert;
});

return $asserts;
$this->assertFileAsserts($assertType, $file, ...$args);
}

public static function getAdditionalConfigFiles(): array
Expand Down

0 comments on commit 3af8630

Please sign in to comment.