From 4eecc4f7b8ff7cfbeaaac73d60876ea759ef0e12 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 6 Oct 2020 16:59:41 +0200 Subject: [PATCH] Report nonexistent classes and functions based on `@since` and `@removed` in phpstorm-stubs while respecting `phpVersion` parameter --- build/baseline-7.4.neon | 2 +- composer.json | 2 +- composer.lock | 16 +++--- .../BetterReflectionSourceLocatorFactory.php | 5 +- .../PhpVersionBlacklistSourceLocator.php | 50 +++++++++++++++++++ .../Runtime/RuntimeReflectionProvider.php | 13 ++++- src/Testing/TestCase.php | 3 +- src/Testing/TestCaseSourceLocatorFactory.php | 5 +- .../Analyser/AnalyserPhp74IntegrationTest.php | 38 ++++++++++++++ .../Analyser/AnalyserPhp80IntegrationTest.php | 38 ++++++++++++++ .../PHPStan/Analyser/data/removedOnPhp80.php | 7 +++ tests/PHPStan/Analyser/php74.neon | 2 + tests/PHPStan/Analyser/php80.neon | 2 + tests/PHPStan/Broker/BrokerTest.php | 4 +- 14 files changed, 169 insertions(+), 18 deletions(-) create mode 100644 src/Reflection/BetterReflection/SourceLocator/PhpVersionBlacklistSourceLocator.php create mode 100644 tests/PHPStan/Analyser/AnalyserPhp74IntegrationTest.php create mode 100644 tests/PHPStan/Analyser/AnalyserPhp80IntegrationTest.php create mode 100644 tests/PHPStan/Analyser/data/removedOnPhp80.php create mode 100644 tests/PHPStan/Analyser/php74.neon create mode 100644 tests/PHPStan/Analyser/php80.neon diff --git a/build/baseline-7.4.neon b/build/baseline-7.4.neon index d3b800b492..6d7d866deb 100644 --- a/build/baseline-7.4.neon +++ b/build/baseline-7.4.neon @@ -89,6 +89,6 @@ parameters: count: 1 path: ../src/Reflection/ReflectionProvider/SetterReflectionProviderProvider.php - - message: "#^Class class@anonymous/src/Testing/TestCase\\.php\\:257 has an uninitialized property \\$reflectionProvider\\. Give it default value or assign it in the constructor\\.$#" + message: "#^Class class@anonymous/src/Testing/TestCase\\.php\\:258 has an uninitialized property \\$reflectionProvider\\. Give it default value or assign it in the constructor\\.$#" count: 1 path: ../src/Testing/TestCase.php diff --git a/composer.json b/composer.json index fc91a355fe..d246542e60 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "nette/utils": "^3.1.3", "nikic/php-parser": "4.10.2", "ondram/ci-detector": "^3.4.0", - "ondrejmirtes/better-reflection": "4.3.33", + "ondrejmirtes/better-reflection": "4.3.34", "phpdocumentor/reflection-docblock": "4.3.4", "phpstan/php-8-stubs": "^0.1.2", "phpstan/phpdoc-parser": "^0.4.9", diff --git a/composer.lock b/composer.lock index 532fe87ffa..de03870d02 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d16f31f33d418639ab566154f3426593", + "content-hash": "78d0c5d1a14fd2d2632288e94165ebb6", "packages": [ { "name": "clue/block-react", @@ -2073,16 +2073,16 @@ }, { "name": "ondrejmirtes/better-reflection", - "version": "4.3.33", + "version": "4.3.34", "source": { "type": "git", "url": "https://github.com/ondrejmirtes/BetterReflection.git", - "reference": "ee91b863e3b20db06b2e1c2240aab02169d8cbe2" + "reference": "a99ec67fbe66e871acec79ed110790e82b681ede" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ondrejmirtes/BetterReflection/zipball/ee91b863e3b20db06b2e1c2240aab02169d8cbe2", - "reference": "ee91b863e3b20db06b2e1c2240aab02169d8cbe2", + "url": "https://api.github.com/repos/ondrejmirtes/BetterReflection/zipball/a99ec67fbe66e871acec79ed110790e82b681ede", + "reference": "a99ec67fbe66e871acec79ed110790e82b681ede", "shasum": "" }, "require": { @@ -2095,7 +2095,7 @@ "roave/signature": "1.1.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.25", + "phpstan/phpstan": "^0.12.49", "phpunit/phpunit": "^7.5.18", "vimeo/psalm": "3.11.2" }, @@ -2141,9 +2141,9 @@ ], "description": "Better Reflection - an improved code reflection API", "support": { - "source": "https://github.com/ondrejmirtes/BetterReflection/tree/4.3.33" + "source": "https://github.com/ondrejmirtes/BetterReflection/tree/4.3.34" }, - "time": "2020-10-05T14:17:19+00:00" + "time": "2020-10-06T14:56:18+00:00" }, { "name": "phpdocumentor/reflection-common", diff --git a/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php b/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php index 575d2db0e2..98aa5970ac 100644 --- a/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php +++ b/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php @@ -10,6 +10,7 @@ use PHPStan\Reflection\BetterReflection\SourceLocator\ComposerJsonAndInstalledJsonSourceLocatorMaker; use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedDirectorySourceLocatorRepository; use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocatorRepository; +use PHPStan\Reflection\BetterReflection\SourceLocator\PhpVersionBlacklistSourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\SkipClassAliasSourceLocator; use Roave\BetterReflection\Reflector\FunctionReflector; use Roave\BetterReflection\SourceLocator\Ast\Locator; @@ -174,8 +175,8 @@ public function create(): SourceLocator $locators[] = $locator; } $locators[] = new ClassWhitelistSourceLocator($this->autoloadSourceLocator, $this->staticReflectionClassNamePatterns); - $locators[] = new PhpInternalSourceLocator($astLocator, $this->reflectionSourceStubber); - $locators[] = new EvaledCodeSourceLocator($astLocator, $this->reflectionSourceStubber); + $locators[] = new PhpVersionBlacklistSourceLocator(new PhpInternalSourceLocator($astLocator, $this->reflectionSourceStubber), $this->phpstormStubsSourceStubber); + $locators[] = new PhpVersionBlacklistSourceLocator(new EvaledCodeSourceLocator($astLocator, $this->reflectionSourceStubber), $this->phpstormStubsSourceStubber); return new MemoizingSourceLocator(new AggregateSourceLocator($locators)); } diff --git a/src/Reflection/BetterReflection/SourceLocator/PhpVersionBlacklistSourceLocator.php b/src/Reflection/BetterReflection/SourceLocator/PhpVersionBlacklistSourceLocator.php new file mode 100644 index 0000000000..4fe8c40bda --- /dev/null +++ b/src/Reflection/BetterReflection/SourceLocator/PhpVersionBlacklistSourceLocator.php @@ -0,0 +1,50 @@ +sourceLocator = $sourceLocator; + $this->phpStormStubsSourceStubber = $phpStormStubsSourceStubber; + } + + public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?Reflection + { + if ($identifier->isClass()) { + if ($this->phpStormStubsSourceStubber->isPresentClass($identifier->getName()) === false) { + return null; + } + } + + if ($identifier->isFunction()) { + if ($this->phpStormStubsSourceStubber->isPresentFunction($identifier->getName()) === false) { + return null; + } + } + + return $this->sourceLocator->locateIdentifier($reflector, $identifier); + } + + public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array + { + return $this->sourceLocator->locateIdentifiersByType($reflector, $identifierType); + } + +} diff --git a/src/Reflection/Runtime/RuntimeReflectionProvider.php b/src/Reflection/Runtime/RuntimeReflectionProvider.php index 20640f046d..0b5383967c 100644 --- a/src/Reflection/Runtime/RuntimeReflectionProvider.php +++ b/src/Reflection/Runtime/RuntimeReflectionProvider.php @@ -18,6 +18,7 @@ use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Type; use ReflectionClass; +use Roave\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber; class RuntimeReflectionProvider implements ReflectionProvider { @@ -37,6 +38,8 @@ class RuntimeReflectionProvider implements ReflectionProvider private StubPhpDocProvider $stubPhpDocProvider; + private PhpStormStubsSourceStubber $phpStormStubsSourceStubber; + /** @var \PHPStan\Reflection\FunctionReflection[] */ private array $functionReflections = []; @@ -55,7 +58,8 @@ public function __construct( FunctionReflectionFactory $functionReflectionFactory, FileTypeMapper $fileTypeMapper, NativeFunctionReflectionProvider $nativeFunctionReflectionProvider, - StubPhpDocProvider $stubPhpDocProvider + StubPhpDocProvider $stubPhpDocProvider, + PhpStormStubsSourceStubber $phpStormStubsSourceStubber ) { $this->reflectionProviderProvider = $reflectionProviderProvider; @@ -64,6 +68,7 @@ public function __construct( $this->fileTypeMapper = $fileTypeMapper; $this->nativeFunctionReflectionProvider = $nativeFunctionReflectionProvider; $this->stubPhpDocProvider = $stubPhpDocProvider; + $this->phpStormStubsSourceStubber = $phpStormStubsSourceStubber; } public function getClass(string $className): \PHPStan\Reflection\ClassReflection @@ -287,9 +292,13 @@ private function getCustomFunction(\PhpParser\Node\Name $nameNode, ?Scope $scope public function resolveFunctionName(\PhpParser\Node\Name $nameNode, ?Scope $scope): ?string { - return $this->resolveName($nameNode, static function (string $name): bool { + return $this->resolveName($nameNode, function (string $name): bool { $exists = function_exists($name); if ($exists) { + if ($this->phpStormStubsSourceStubber->isPresentFunction($name) === false) { + return false; + } + return true; } diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index 0ec34f71ed..2925c08682 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -221,7 +221,8 @@ private function createRuntimeReflectionProvider(ReflectionProvider $actualRefle $functionReflectionFactory, $fileTypeMapper, self::getContainer()->getByType(NativeFunctionReflectionProvider::class), - self::getContainer()->getByType(StubPhpDocProvider::class) + self::getContainer()->getByType(StubPhpDocProvider::class), + self::getContainer()->getByType(PhpStormStubsSourceStubber::class) ), self::getPhpStormStubsSourceStubber(), [ diff --git a/src/Testing/TestCaseSourceLocatorFactory.php b/src/Testing/TestCaseSourceLocatorFactory.php index d25cbc1c77..fda53c3c5b 100644 --- a/src/Testing/TestCaseSourceLocatorFactory.php +++ b/src/Testing/TestCaseSourceLocatorFactory.php @@ -6,6 +6,7 @@ use PHPStan\DependencyInjection\Container; use PHPStan\Reflection\BetterReflection\SourceLocator\AutoloadSourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\ComposerJsonAndInstalledJsonSourceLocatorMaker; +use PHPStan\Reflection\BetterReflection\SourceLocator\PhpVersionBlacklistSourceLocator; use Roave\BetterReflection\Reflector\FunctionReflector; use Roave\BetterReflection\SourceLocator\Ast\Locator; use Roave\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber; @@ -74,8 +75,8 @@ public function create(): SourceLocator $locators[] = new PhpInternalSourceLocator($astLocator, $this->phpstormStubsSourceStubber); $locators[] = $this->autoloadSourceLocator; - $locators[] = new PhpInternalSourceLocator($astLocator, $this->reflectionSourceStubber); - $locators[] = new EvaledCodeSourceLocator($astLocator, $this->reflectionSourceStubber); + $locators[] = new PhpVersionBlacklistSourceLocator(new PhpInternalSourceLocator($astLocator, $this->reflectionSourceStubber), $this->phpstormStubsSourceStubber); + $locators[] = new PhpVersionBlacklistSourceLocator(new EvaledCodeSourceLocator($astLocator, $this->reflectionSourceStubber), $this->phpstormStubsSourceStubber); return new MemoizingSourceLocator(new AggregateSourceLocator($locators)); } diff --git a/tests/PHPStan/Analyser/AnalyserPhp74IntegrationTest.php b/tests/PHPStan/Analyser/AnalyserPhp74IntegrationTest.php new file mode 100644 index 0000000000..bec2eaa9d7 --- /dev/null +++ b/tests/PHPStan/Analyser/AnalyserPhp74IntegrationTest.php @@ -0,0 +1,38 @@ +runAnalyse(__DIR__ . '/data/removedOnPhp80.php'); + $this->assertCount(0, $errors); + } + + /** + * @return string[] + */ + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/php74.neon', + ]; + } + + + /** + * @param string $file + * @return \PHPStan\Analyser\Error[] + */ + private function runAnalyse(string $file): array + { + $file = $this->getFileHelper()->normalizePath($file); + /** @var \PHPStan\Analyser\Analyser $analyser */ + $analyser = self::getContainer()->getByType(Analyser::class); + + return $analyser->analyse([$file])->getErrors(); + } + +} diff --git a/tests/PHPStan/Analyser/AnalyserPhp80IntegrationTest.php b/tests/PHPStan/Analyser/AnalyserPhp80IntegrationTest.php new file mode 100644 index 0000000000..011cab463b --- /dev/null +++ b/tests/PHPStan/Analyser/AnalyserPhp80IntegrationTest.php @@ -0,0 +1,38 @@ +runAnalyse(__DIR__ . '/data/removedOnPhp80.php'); + $this->assertCount(2, $errors); + } + + /** + * @return string[] + */ + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/php80.neon', + ]; + } + + + /** + * @param string $file + * @return \PHPStan\Analyser\Error[] + */ + private function runAnalyse(string $file): array + { + $file = $this->getFileHelper()->normalizePath($file); + /** @var \PHPStan\Analyser\Analyser $analyser */ + $analyser = self::getContainer()->getByType(Analyser::class); + + return $analyser->analyse([$file])->getErrors(); + } + +} diff --git a/tests/PHPStan/Analyser/data/removedOnPhp80.php b/tests/PHPStan/Analyser/data/removedOnPhp80.php new file mode 100644 index 0000000000..5ac63f1b45 --- /dev/null +++ b/tests/PHPStan/Analyser/data/removedOnPhp80.php @@ -0,0 +1,7 @@ +createMock(FunctionReflectionFactory::class), new FileTypeMapper($setterReflectionProviderProvider, $this->getParser(), $phpDocStringResolver, $phpDocNodeResolver, $this->createMock(Cache::class), $anonymousClassNameHelper), self::getContainer()->getByType(NativeFunctionReflectionProvider::class), - self::getContainer()->getByType(StubPhpDocProvider::class) + self::getContainer()->getByType(StubPhpDocProvider::class), + self::getContainer()->getByType(PhpStormStubsSourceStubber::class) ); $setterReflectionProviderProvider->setReflectionProvider($reflectionProvider); $this->broker = new Broker(