add test for internal @meta tag. php7.4 migration

This commit is contained in:
Ivan Fedorov 2020-01-19 21:23:34 +03:00
parent afd7a81f83
commit 29235cf84c
24 changed files with 200 additions and 80 deletions

View File

@ -14,7 +14,7 @@
"inspection"
],
"require-dev": {
"php": "^7.1",
"php": "^7.4",
"nikic/php-parser": "^4",
"phpdocumentor/reflection-docblock": "^4.3",
"phpunit/phpunit": "^7"

View File

@ -10,10 +10,10 @@ abstract class BasePHPClass extends BasePHPElement
/**
* @var PHPMethod[]
*/
public $methods = [];
public array $methods = [];
/**
* @var PHPConst[]
*/
public $constants = [];
public array $constants = [];
}

View File

@ -3,12 +3,14 @@ declare(strict_types=1);
namespace StubTests\Model;
use Exception;
abstract class BasePHPElement
{
public $name;
public $stubBelongsToCore = false;
public $parseError;
protected $mutedProblems = [];
public string $name;
public bool $stubBelongsToCore = false;
public ?Exception $parseError = null;
protected array $mutedProblems = [];
/**
* @param mixed $object

View File

@ -5,15 +5,13 @@ namespace StubTests\Model;
use PhpParser\Node\Stmt\Class_;
use ReflectionClass;
use ReflectionClassConstant;
use ReflectionException;
use ReflectionMethod;
use stdClass;
class PHPClass extends BasePHPClass
{
public $parentClass;
public $interfaces = [];
public array $interfaces = [];
/**
* @param ReflectionClass $clazz
@ -30,7 +28,6 @@ class PHPClass extends BasePHPClass
}
$this->interfaces = $reflectionClass->getInterfaceNames();
/**@var ReflectionMethod $method */
foreach ($reflectionClass->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() !== $this->name) {
continue;
@ -38,7 +35,6 @@ class PHPClass extends BasePHPClass
$this->methods[$method->name] = (new PHPMethod())->readObjectFromReflection($method);
}
/**@var ReflectionClassConstant $constant */
foreach ($reflectionClass->getReflectionConstants() as $constant) {
if ($constant->getDeclaringClass()->getName() !== $this->name) {
continue;

View File

@ -1,22 +1,21 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
use PhpParser\Node\Const_;
use PhpParser\Node\Expr\UnaryMinus;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\NodeAbstract;
use ReflectionClassConstant;
use stdClass;
use PhpParser\Node\Expr\UnaryMinus;
class PHPConst extends BasePHPElement
{
use PHPDocElement;
public $parentName;
public ?string $parentName = null;
public $value;
/**

View File

@ -13,22 +13,24 @@ trait PHPDocElement
/**
* @var Tag[]
*/
public $links = [];
public array $links = [];
/**
* @var Tag[]
*/
public $see = [];
public array $see = [];
/**
* @var Tag[]
*/
public $sinceTags = [];
public array $sinceTags = [];
/**
* @var Tag[]
*/
public $deprecatedTags = [];
public array $deprecatedTags = [];
public bool $hasInternalMetaTag = false;
protected function collectLinks(Node $node): void
{
@ -38,7 +40,7 @@ trait PHPDocElement
$this->links = $phpDoc->getTagsByName('link');
$this->see = $phpDoc->getTagsByName('see');
} catch (Exception $e) {
$this->parseError = $e->getMessage();
$this->parseError = $e;
}
}
}
@ -51,7 +53,19 @@ trait PHPDocElement
$this->sinceTags = $phpDoc->getTagsByName('since');
$this->deprecatedTags = $phpDoc->getTagsByName('deprecated');
} catch (Exception $e) {
$this->parseError = $e->getMessage();
$this->parseError = $e;
}
}
}
protected function checkIfHasInternalMetaTag(Node $node): void
{
if ($node->getDocComment() !== null) {
try {
$phpDoc = DocFactoryProvider::getDocFactory()->create($node->getDocComment()->getText());
$this->hasInternalMetaTag = $phpDoc->hasTag('meta');
} catch (Exception $e) {
$this->parseError = $e;
}
}
}

View File

@ -10,7 +10,6 @@ use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\Function_;
use ReflectionException;
use ReflectionFunction;
use ReflectionParameter;
use stdClass;
use StubTests\Parsers\DocFactoryProvider;
@ -21,15 +20,15 @@ class PHPFunction extends BasePHPElement
/**
* @var boolean $is_deprecated
*/
public $is_deprecated;
public bool $is_deprecated;
/**
* @var PHPParameter[]
*/
public $parameters = [];
public array $parameters = [];
/**
* @var Type $returnTag
*/
public $returnTag;
public ?Type $returnTag = null;
/**
* @param ReflectionFunction $function
@ -41,7 +40,6 @@ class PHPFunction extends BasePHPElement
$reflectionFunction = new ReflectionFunction($function);
$this->name = $reflectionFunction->name;
$this->is_deprecated = $reflectionFunction->isDeprecated();
/**@var ReflectionParameter $parameter */
foreach ($reflectionFunction->getParameters() as $parameter) {
$this->parameters[] = (new PHPParameter())->readObjectFromReflection($parameter);
}
@ -66,6 +64,7 @@ class PHPFunction extends BasePHPElement
$this->collectLinks($node);
$this->collectSinceDeprecatedVersions($node);
$this->checkIfHasInternalMetaTag($node);
$this->checkDeprecationTag($node);
$this->checkReturnTag($node);
return $this;
@ -82,7 +81,7 @@ class PHPFunction extends BasePHPElement
$this->is_deprecated = true;
}
} catch (Exception $e) {
$this->parseError = $e->getMessage();
$this->parseError = $e;
}
}
}
@ -94,10 +93,10 @@ class PHPFunction extends BasePHPElement
$phpDoc = DocFactoryProvider::getDocFactory()->create($node->getDocComment()->getText());
$parsedReturnTag = $phpDoc->getTagsByName('return');
if (!empty($parsedReturnTag) && $parsedReturnTag[0] instanceof Return_) {
$this->returnTag = $parsedReturnTag[0]->getType() . '';
$this->returnTag = $parsedReturnTag[0]->getType();
}
} catch (Exception $e) {
$this->parseError = $e->getMessage();
$this->parseError = $e;
}
}
}

View File

@ -5,14 +5,12 @@ namespace StubTests\Model;
use PhpParser\Node\Stmt\Interface_;
use ReflectionClass;
use ReflectionClassConstant;
use ReflectionException;
use ReflectionMethod;
use stdClass;
class PHPInterface extends BasePHPClass
{
public $parentInterfaces = [];
public array $parentInterfaces = [];
/**
* @param ReflectionClass $interface
@ -23,7 +21,6 @@ class PHPInterface extends BasePHPClass
try {
$reflectionInterface = new ReflectionClass($interface);
$this->name = $reflectionInterface->getName();
/**@var ReflectionMethod $method */
foreach ($reflectionInterface->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() !== $this->name) {
continue;
@ -31,7 +28,6 @@ class PHPInterface extends BasePHPClass
$this->methods[$method->name] = (new PHPMethod())->readObjectFromReflection($method);
}
$this->parentInterfaces = $reflectionInterface->getInterfaceNames();
/**@var ReflectionClassConstant $constant */
foreach ($reflectionInterface->getReflectionConstants() as $constant) {
if ($constant->getDeclaringClass()->getName() !== $this->name) {
continue;

View File

@ -4,18 +4,18 @@ declare(strict_types=1);
namespace StubTests\Model;
use PhpParser\Node\Stmt\ClassMethod;
use ReflectionParameter;
use ReflectionMethod;
use stdClass;
class PHPMethod extends PHPFunction
{
public $access;
public $is_static;
public $is_final;
public $parentName;
public string $access;
public bool $is_static;
public bool $is_final;
public string $parentName;
/**
* @param \ReflectionMethod $method
* @param ReflectionMethod $method
* @return $this
*/
public function readObjectFromReflection($method)
@ -23,7 +23,6 @@ class PHPMethod extends PHPFunction
$this->name = $method->name;
$this->is_static = $method->isStatic();
$this->is_final = $method->isFinal();
/**@var ReflectionParameter $parameter */
foreach ($method->getParameters() as $parameter) {
$this->parameters[] = (new PHPParameter())->readObjectFromReflection($parameter);
}
@ -50,6 +49,7 @@ class PHPMethod extends PHPFunction
$this->collectLinks($node);
$this->collectSinceDeprecatedVersions($node);
$this->checkIfHasInternalMetaTag($node);
$this->checkDeprecationTag($node);
$this->checkReturnTag($node);

View File

@ -4,14 +4,15 @@ declare(strict_types=1);
namespace StubTests\Model;
use PhpParser\Node\Param;
use ReflectionNamedType;
use ReflectionParameter;
use stdClass;
class PHPParameter extends BasePHPElement
{
public $type = '';
public $is_vararg;
public $is_passed_by_ref;
public string $type = '';
public bool $is_vararg;
public bool $is_passed_by_ref;
/**
* @param ReflectionParameter $parameter
@ -20,8 +21,9 @@ class PHPParameter extends BasePHPElement
public function readObjectFromReflection($parameter): self
{
$this->name = $parameter->name;
if (!empty($parameter->getType())) {
$this->type = $parameter->getType()->getName();
$parameterType = $parameter->getType();
if (!empty($parameterType) && $parameterType instanceof ReflectionNamedType) {
$this->type = $parameterType->getName();
}
$this->is_vararg = $parameter->isVariadic();
$this->is_passed_by_ref = $parameter->isPassedByReference();

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace StubTests\Model;
use Exception;
use function array_key_exists;
class StubsContainer
@ -10,19 +11,19 @@ class StubsContainer
/**
* @var PHPConst[]
*/
private $constants = [];
private array $constants = [];
/**
* @var PHPFunction[]
*/
private $functions = [];
private array $functions = [];
/**
* @var PHPClass[]
*/
private $classes = [];
private array $classes = [];
/**
* @var PHPInterface[]
*/
private $interfaces = [];
private array $interfaces = [];
/**
* @return PHPConst[]
@ -34,11 +35,12 @@ class StubsContainer
/**
* @param PHPConst $constant
* @throws Exception
*/
public function addConstant(PHPConst $constant): void
{
if (array_key_exists($constant->name, $this->constants)) {
throw new \Exception($constant->name . " is already defined in stubs");
throw new Exception($constant->name . " is already defined in stubs");
}
$this->constants[$constant->name] = $constant;
}
@ -61,7 +63,7 @@ class StubsContainer
/**
* @param string $name
* @return PHPClass | null
* @return PHPClass|null
*/
public function getClass(string $name): ?PHPClass
{
@ -82,18 +84,19 @@ class StubsContainer
/**
* @param PHPClass $class
* @throws Exception
*/
public function addClass(PHPClass $class): void
{
if (array_key_exists($class->name, $this->classes)) {
throw new \Exception($class->name . " is already defined in stubs");
throw new Exception($class->name . " is already defined in stubs");
}
$this->classes[$class->name] = $class;
}
/**
* @param string $name
* @return PHPInterface | null
* @return PHPInterface|null
*/
public function getInterface(string $name): ?PHPInterface
{
@ -114,11 +117,12 @@ class StubsContainer
/**
* @param PHPInterface $interface
* @throws Exception
*/
public function addInterface(PHPInterface $interface): void
{
if (array_key_exists($interface->name, $this->interfaces)) {
throw new \Exception($interface->name . " is already defined in stubs");
throw new Exception($interface->name . " is already defined in stubs");
}
$this->interfaces[$interface->name] = $interface;
}

View File

@ -7,7 +7,7 @@ use phpDocumentor\Reflection\DocBlockFactory;
class DocFactoryProvider
{
private static $docFactory;
private static ?DocBlockFactory $docFactory = null;
public static function getDocFactory(): DocBlockFactory
{

View File

@ -8,16 +8,16 @@ class ExpectedFunctionArgumentsInfo
/**
* @var Expr|null
*/
private $functionReference;
private ?Expr $functionReference;
/**
* @var Expr[]
*/
private $expectedArguments;
private array $expectedArguments;
/**
* @var int
*/
private $index;
private int $index;
/**
* ExpectedFunctionArgumentsInfo constructor.

View File

@ -19,11 +19,11 @@ class MetaExpectedArgumentsCollector extends NodeVisitorAbstract
/**
* @var ExpectedFunctionArgumentsInfo[]
*/
private $expectedArgumentsInfos;
private array $expectedArgumentsInfos;
/**
* @var String[]
*/
private $registeredArgumentsSet;
private array $registeredArgumentsSet;
public function __construct()
{

View File

@ -21,7 +21,7 @@ use UnexpectedValueException;
class StubParser
{
private static $stubs;
private static ?StubsContainer $stubs = null;
public static function getPhpStormStubs(): StubsContainer
{
@ -30,7 +30,7 @@ class StubParser
$coreStubVisitor = new CoreStubASTVisitor(self::$stubs);
/** @noinspection PhpUnhandledExceptionInspection */
self::processStubs($visitor, $coreStubVisitor, function ($file) {
return true;
return $file->getFilename() !== '.phpstorm.meta.php';
});
foreach (self::$stubs->getInterfaces() as $interface) {
$interface->parentInterfaces = $visitor->combineParentInterfaces($interface);

View File

@ -22,8 +22,8 @@ use StubTests\Parsers\Utils;
class ASTVisitor extends NodeVisitorAbstract
{
protected $stubs;
protected $isStubCore;
protected StubsContainer $stubs;
protected bool $isStubCore;
public function __construct(StubsContainer $stubs)
{

View File

@ -1,9 +1,8 @@
<?php
declare(strict_types=1);
namespace StubTests\Parsers\Visitors;
use StubTests\Model\StubsContainer;
class CoreStubASTVisitor extends ASTVisitor
@ -13,4 +12,4 @@ class CoreStubASTVisitor extends ASTVisitor
parent::__construct($stubs);
$this->isStubCore = true;
}
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace StubTests\Parsers\Visitors;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\NodeVisitorAbstract;
use RuntimeException;
use SplFileInfo;
use StubTests\Parsers\StubParser;
class MetaOverrideFunctionsParser extends NodeVisitorAbstract
{
private const OVERRIDE_FUNCTION = 'override';
/**
* @var string[]
*/
public array $overridenFunctions;
public function __construct()
{
$this->overridenFunctions = [];
StubParser::processStubs($this, null, function (SplFileInfo $file) {
return $file->getFilename() === '.phpstorm.meta.php';
});
}
public function enterNode(Node $node)
{
if ($node instanceof Node\Expr\FuncCall) {
if ((string)$node->name === self::OVERRIDE_FUNCTION) {
$args = $node->args;
if (count($args) < 2) {
throw new RuntimeException('Expected at least 2 arguments for override call');
}
$this->overridenFunctions[] = $this->getOverrideFunctionName($args[0]);
}
}
}
private function getOverrideFunctionName($param)
{
$paramValue = $param->value;
$targetFunction = null;
if ($paramValue instanceof Expr\StaticCall) {
$targetFunction = $paramValue->class . "::" . $paramValue->name;
} else {
$targetFunction = (string)$paramValue->name;
}
return $targetFunction;
}
}

View File

@ -11,7 +11,10 @@ use PhpParser\NodeVisitorAbstract;
*/
class ParentConnector extends NodeVisitorAbstract
{
private $stack;
/**
* @var Node[]
*/
private array $stack;
public function beforeTraverse(array $nodes)
{

View File

@ -7,7 +7,6 @@ use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_;
use PHPUnit\Framework\TestCase;
use StubTests\Model\PHPConst;
@ -21,14 +20,14 @@ class StubsMetaExpectedArgumentsTest extends TestCase
/**
* @var ExpectedFunctionArgumentsInfo[]
*/
private static $expectedArguments;
private static array $expectedArguments;
/**
* @var String[]
*/
private static $registeredArgumentsSet;
private static array $registeredArgumentsSet;
private static $functionsFqns;
private static $methodsFqns;
private static $constantsFqns;
private static array $methodsFqns;
private static array $constantsFqns;
public static function setUpBeforeClass(): void
{

View File

@ -0,0 +1,53 @@
<?php
namespace StubTests;
use PHPUnit\Framework\TestCase;
use StubTests\Parsers\Visitors\MetaOverrideFunctionsParser;
use StubTests\TestData\Providers\PhpStormStubsSingleton;
class StubsMetaInternalTagTest extends TestCase
{
/**
* @var array
*/
private static array $overridenFunctionsInMeta;
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
self::$overridenFunctionsInMeta = (new MetaOverrideFunctionsParser())->overridenFunctions;
}
public function testFunctionInternalMetaTag(): void
{
$functions = PhpStormStubsSingleton::getPhpStormStubs()->getFunctions();
foreach ($functions as $function) {
if ($function->hasInternalMetaTag) {
$this->checkInternalMetaInOverride($function->name);
}
}
}
public function testMethodsInternalMetaTag(): void
{
foreach (PhpStormStubsSingleton::getPhpStormStubs()->getClasses() as $className => $class) {
foreach ($class->methods as $methodName => $method) {
if ($method->hasInternalMetaTag) {
$this->checkInternalMetaInOverride($className . "::" . $methodName);
} else {
$this->expectNotToPerformAssertions();
}
}
}
}
/**
* @param string $elementName
*/
private function checkInternalMetaInOverride(string $elementName): void
{
self::assertContains($elementName, self::$overridenFunctionsInMeta,
"$elementName contains @meta in phpdoc but isn't added to 'override()' functions in meta file");
}
}

View File

@ -1,14 +1,13 @@
<?php
declare(strict_types=1);
namespace StubTests\TestData\Providers;
use StubTests\Parsers\Utils;
class PhpCoreStubsProvider
{
private static $StubDirectoryMap = [
private static array $StubDirectoryMap = [
'CORE' => [
'Core',
'date',
@ -153,4 +152,4 @@ class PhpCoreStubsProvider
return Utils::flattenArray($coreStubs, false);
}
}
}

View File

@ -1,4 +1,6 @@
<?php
declare(strict_types=1);
namespace StubTests\TestData\Providers;
use StubTests\Model\StubsContainer;
@ -6,7 +8,7 @@ use StubTests\Parsers\StubParser;
class PhpStormStubsSingleton
{
private static $phpstormStubs;
private static ?StubsContainer $phpstormStubs = null;
public static function getPhpStormStubs(): StubsContainer
{

View File

@ -43,7 +43,7 @@ class ReflectionTestDataProviders
class ReflectionStubsSingleton
{
private static $reflectionStubs;
private static ?StubsContainer $reflectionStubs = null;
public static function getReflectionStubs(): StubsContainer
{