diff --git a/composer.json b/composer.json index ece19db2..e4b7575c 100644 --- a/composer.json +++ b/composer.json @@ -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" diff --git a/tests/Model/BasePHPClass.php b/tests/Model/BasePHPClass.php index 2a6bc4b3..081bde97 100644 --- a/tests/Model/BasePHPClass.php +++ b/tests/Model/BasePHPClass.php @@ -10,10 +10,10 @@ abstract class BasePHPClass extends BasePHPElement /** * @var PHPMethod[] */ - public $methods = []; + public array $methods = []; /** * @var PHPConst[] */ - public $constants = []; + public array $constants = []; } diff --git a/tests/Model/BasePHPElement.php b/tests/Model/BasePHPElement.php index 643def5f..6e7affd9 100644 --- a/tests/Model/BasePHPElement.php +++ b/tests/Model/BasePHPElement.php @@ -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 diff --git a/tests/Model/PHPClass.php b/tests/Model/PHPClass.php index 77b2a1d5..8c443f35 100644 --- a/tests/Model/PHPClass.php +++ b/tests/Model/PHPClass.php @@ -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; diff --git a/tests/Model/PHPConst.php b/tests/Model/PHPConst.php index 6c7be362..75e1d853 100644 --- a/tests/Model/PHPConst.php +++ b/tests/Model/PHPConst.php @@ -1,22 +1,21 @@ 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; } } } diff --git a/tests/Model/PHPFunction.php b/tests/Model/PHPFunction.php index 80a4445d..65672371 100644 --- a/tests/Model/PHPFunction.php +++ b/tests/Model/PHPFunction.php @@ -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; } } } diff --git a/tests/Model/PHPInterface.php b/tests/Model/PHPInterface.php index 6bb4bfc1..8b2f7c16 100644 --- a/tests/Model/PHPInterface.php +++ b/tests/Model/PHPInterface.php @@ -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; diff --git a/tests/Model/PHPMethod.php b/tests/Model/PHPMethod.php index 43f04efa..e838ec28 100644 --- a/tests/Model/PHPMethod.php +++ b/tests/Model/PHPMethod.php @@ -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); diff --git a/tests/Model/PHPParameter.php b/tests/Model/PHPParameter.php index 71855a5b..79b5a2ea 100644 --- a/tests/Model/PHPParameter.php +++ b/tests/Model/PHPParameter.php @@ -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(); diff --git a/tests/Model/StubsContainer.php b/tests/Model/StubsContainer.php index ed9bc7ce..43023c88 100644 --- a/tests/Model/StubsContainer.php +++ b/tests/Model/StubsContainer.php @@ -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; } diff --git a/tests/Parsers/DocFactoryProvider.php b/tests/Parsers/DocFactoryProvider.php index b474bc77..c1e7d4d2 100644 --- a/tests/Parsers/DocFactoryProvider.php +++ b/tests/Parsers/DocFactoryProvider.php @@ -7,7 +7,7 @@ use phpDocumentor\Reflection\DocBlockFactory; class DocFactoryProvider { - private static $docFactory; + private static ?DocBlockFactory $docFactory = null; public static function getDocFactory(): DocBlockFactory { diff --git a/tests/Parsers/ExpectedFunctionArgumentsInfo.php b/tests/Parsers/ExpectedFunctionArgumentsInfo.php index dd5bb587..d9eb7a0a 100644 --- a/tests/Parsers/ExpectedFunctionArgumentsInfo.php +++ b/tests/Parsers/ExpectedFunctionArgumentsInfo.php @@ -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. diff --git a/tests/Parsers/MetaExpectedArgumentsCollector.php b/tests/Parsers/MetaExpectedArgumentsCollector.php index 772d2190..9bca914f 100644 --- a/tests/Parsers/MetaExpectedArgumentsCollector.php +++ b/tests/Parsers/MetaExpectedArgumentsCollector.php @@ -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() { diff --git a/tests/Parsers/StubParser.php b/tests/Parsers/StubParser.php index 4ff7a699..fdd3b32e 100644 --- a/tests/Parsers/StubParser.php +++ b/tests/Parsers/StubParser.php @@ -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); diff --git a/tests/Parsers/Visitors/ASTVisitor.php b/tests/Parsers/Visitors/ASTVisitor.php index 8c3d7780..f19dd1dc 100644 --- a/tests/Parsers/Visitors/ASTVisitor.php +++ b/tests/Parsers/Visitors/ASTVisitor.php @@ -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) { diff --git a/tests/Parsers/Visitors/CoreStubASTVisitor.php b/tests/Parsers/Visitors/CoreStubASTVisitor.php index eaf0d73f..15a2e038 100644 --- a/tests/Parsers/Visitors/CoreStubASTVisitor.php +++ b/tests/Parsers/Visitors/CoreStubASTVisitor.php @@ -1,9 +1,8 @@ isStubCore = true; } -} \ No newline at end of file +} diff --git a/tests/Parsers/Visitors/MetaOverrideFunctionsParser.php b/tests/Parsers/Visitors/MetaOverrideFunctionsParser.php new file mode 100644 index 00000000..30a5858f --- /dev/null +++ b/tests/Parsers/Visitors/MetaOverrideFunctionsParser.php @@ -0,0 +1,53 @@ +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; + } +} diff --git a/tests/Parsers/Visitors/ParentConnector.php b/tests/Parsers/Visitors/ParentConnector.php index 417d487c..6e27584f 100644 --- a/tests/Parsers/Visitors/ParentConnector.php +++ b/tests/Parsers/Visitors/ParentConnector.php @@ -11,7 +11,10 @@ use PhpParser\NodeVisitorAbstract; */ class ParentConnector extends NodeVisitorAbstract { - private $stack; + /** + * @var Node[] + */ + private array $stack; public function beforeTraverse(array $nodes) { diff --git a/tests/StubsMetaExpectedArgumentsTest.php b/tests/StubsMetaExpectedArgumentsTest.php index f9c77bb5..53fe51b7 100644 --- a/tests/StubsMetaExpectedArgumentsTest.php +++ b/tests/StubsMetaExpectedArgumentsTest.php @@ -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 { diff --git a/tests/StubsMetaInternalTagTest.php b/tests/StubsMetaInternalTagTest.php new file mode 100644 index 00000000..05b96691 --- /dev/null +++ b/tests/StubsMetaInternalTagTest.php @@ -0,0 +1,53 @@ +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"); + } +} diff --git a/tests/TestData/Providers/PhpCoreStubsProvider.php b/tests/TestData/Providers/PhpCoreStubsProvider.php index 18633e7a..27319325 100644 --- a/tests/TestData/Providers/PhpCoreStubsProvider.php +++ b/tests/TestData/Providers/PhpCoreStubsProvider.php @@ -1,14 +1,13 @@ [ 'Core', 'date', @@ -153,4 +152,4 @@ class PhpCoreStubsProvider return Utils::flattenArray($coreStubs, false); } -} \ No newline at end of file +} diff --git a/tests/TestData/Providers/PhpStormStubsSingleton.php b/tests/TestData/Providers/PhpStormStubsSingleton.php index 927a471f..bf266c1e 100644 --- a/tests/TestData/Providers/PhpStormStubsSingleton.php +++ b/tests/TestData/Providers/PhpStormStubsSingleton.php @@ -1,4 +1,6 @@