From 3af8630723f11a4683aae788903622c402ac3315 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 20 Apr 2021 10:46:51 +0200 Subject: [PATCH] TypeInfereceTest - allow 3rd parties same modern style of type inference testing as in NodeScopeResolverTest --- src/Testing/TypeInferenceTest.php | 111 ++++++++++++++++++ .../Analyser/NodeScopeResolverTest.php | 99 +--------------- 2 files changed, 112 insertions(+), 98 deletions(-) diff --git a/src/Testing/TypeInferenceTest.php b/src/Testing/TypeInferenceTest.php index 06410ff983..74b860f2f6 100644 --- a/src/Testing/TypeInferenceTest.php +++ b/src/Testing/TypeInferenceTest.php @@ -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; @@ -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 { @@ -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 + */ + 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 { diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index d752ff0c73..a871848477 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -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 { @@ -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 - */ - 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