From ea59386306a18b63bbd6bf5147d363a9b45aa5c5 Mon Sep 17 00:00:00 2001 From: schlndh Date: Fri, 8 Dec 2023 12:00:42 +0100 Subject: [PATCH 1/3] check generic mixed type based on config --- src/Analyser/DirectInternalScopeFactory.php | 3 ++ src/Analyser/LazyInternalScopeFactory.php | 2 + src/Analyser/MutatingScope.php | 4 +- src/Rules/RuleLevelHelper.php | 8 ++-- src/Testing/PHPStanTestCase.php | 31 +++++++++++++ src/Testing/RuleLevelHelperHack.php | 40 ++++++++++++++++ .../AnalyserTraitsIntegrationTest.php | 2 + tests/PHPStan/Analyser/TypeSpecifierTest.php | 2 + .../TraitsCachingIssueIntegrationTest.php | 2 + .../TableErrorFormatterTest.php | 4 ++ .../PhpDoc/DefaultStubFilesProviderTest.php | 2 + ...StanNamespaceIn3rdPartyPackageRuleTest.php | 2 + .../Methods/CallStaticMethodsRuleTest.php | 37 ++++++++++++++- .../Methods/data/generic-instanceof-enum.php | 46 +++++++++++++++++++ 14 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 src/Testing/RuleLevelHelperHack.php create mode 100644 tests/PHPStan/Rules/Methods/data/generic-instanceof-enum.php diff --git a/src/Analyser/DirectInternalScopeFactory.php b/src/Analyser/DirectInternalScopeFactory.php index 07d7fbe09c..e79aada21f 100644 --- a/src/Analyser/DirectInternalScopeFactory.php +++ b/src/Analyser/DirectInternalScopeFactory.php @@ -14,6 +14,7 @@ use PHPStan\Reflection\ParametersAcceptor; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Properties\PropertyReflectionFinder; +use PHPStan\Rules\RuleLevelHelper; use PHPStan\ShouldNotHappenException; use function is_a; @@ -38,6 +39,7 @@ public function __construct( private bool $explicitMixedInUnknownGenericNew, private bool $explicitMixedForGlobalVariables, private ConstantResolver $constantResolver, + private RuleLevelHelper $ruleLevelHelper, ) { } @@ -86,6 +88,7 @@ public function create( $this->parser, $this->nodeScopeResolver, $this->constantResolver, + $this->ruleLevelHelper, $context, $this->phpVersion, $declareStrictTypes, diff --git a/src/Analyser/LazyInternalScopeFactory.php b/src/Analyser/LazyInternalScopeFactory.php index 0d74666e85..da2ec75428 100644 --- a/src/Analyser/LazyInternalScopeFactory.php +++ b/src/Analyser/LazyInternalScopeFactory.php @@ -14,6 +14,7 @@ use PHPStan\Reflection\ParametersAcceptor; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Properties\PropertyReflectionFinder; +use PHPStan\Rules\RuleLevelHelper; use PHPStan\ShouldNotHappenException; use function is_a; @@ -80,6 +81,7 @@ public function create( $this->container->getService('currentPhpVersionSimpleParser'), $this->container->getByType(NodeScopeResolver::class), $this->container->getByType(ConstantResolver::class), + $this->container->getByType(RuleLevelHelper::class), $context, $this->container->getByType(PhpVersion::class), $declareStrictTypes, diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index e920b8b03f..75e4beeacc 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -66,6 +66,7 @@ use PHPStan\Reflection\ReflectionProvider; use PHPStan\Reflection\TrivialParametersAcceptor; use PHPStan\Rules\Properties\PropertyReflectionFinder; +use PHPStan\Rules\RuleLevelHelper; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; use PHPStan\Type\Accessory\AccessoryArrayListType; @@ -188,6 +189,7 @@ public function __construct( private Parser $parser, private NodeScopeResolver $nodeScopeResolver, private ConstantResolver $constantResolver, + private RuleLevelHelper $ruleLevelHelper, private ScopeContext $context, private PhpVersion $phpVersion, private bool $declareStrictTypes = false, @@ -2772,7 +2774,7 @@ private function enterFunctionLike( } } $parameterNode = new Variable($parameter->getName()); - $expressionTypes[$paramExprString] = ExpressionTypeHolder::createYes($parameterNode, $parameterType); + $expressionTypes[$paramExprString] = ExpressionTypeHolder::createYes($parameterNode, $this->ruleLevelHelper->transformCommonType($parameterType)); $nativeParameterType = $parameter->getNativeType(); if ($parameter->isVariadic()) { diff --git a/src/Rules/RuleLevelHelper.php b/src/Rules/RuleLevelHelper.php index 5efd65b20e..28076b6e7b 100644 --- a/src/Rules/RuleLevelHelper.php +++ b/src/Rules/RuleLevelHelper.php @@ -33,10 +33,10 @@ class RuleLevelHelper public function __construct( private ReflectionProvider $reflectionProvider, private bool $checkNullables, - private bool $checkThisOnly, + protected bool $checkThisOnly, private bool $checkUnionTypes, - private bool $checkExplicitMixed, - private bool $checkImplicitMixed, + protected bool $checkExplicitMixed, + protected bool $checkImplicitMixed, private bool $newRuleLevelHelper, private bool $checkBenevolentUnionTypes, ) @@ -55,7 +55,7 @@ public function accepts(Type $acceptingType, Type $acceptedType, bool $strictTyp return $this->acceptsWithReason($acceptingType, $acceptedType, $strictTypes)->result; } - private function transformCommonType(Type $type): Type + public function transformCommonType(Type $type): Type { if (!$this->checkExplicitMixed && !$this->checkImplicitMixed) { return $type; diff --git a/src/Testing/PHPStanTestCase.php b/src/Testing/PHPStanTestCase.php index 14efcc7480..c6ba30178a 100644 --- a/src/Testing/PHPStanTestCase.php +++ b/src/Testing/PHPStanTestCase.php @@ -32,6 +32,7 @@ use PHPStan\Reflection\ReflectionProvider; use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider; use PHPStan\Rules\Properties\PropertyReflectionFinder; +use PHPStan\Rules\RuleLevelHelper; use PHPStan\Type\Constant\OversizedArrayBuilder; use PHPStan\Type\TypeAliasResolver; use PHPStan\Type\UsefulTypeAliasResolver; @@ -57,6 +58,35 @@ abstract class PHPStanTestCase extends TestCase /** @var array */ private static array $containers = []; + private RuleLevelHelper $tearDownRuleLevelHelper; + + private bool $initialCheckExplicitMixed; + + private bool $initialCheckImplicitMixed; + + private bool $initialCheckThisOnly; + + protected function setUp(): void + { + parent::setUp(); + + $this->tearDownRuleLevelHelper = $helper = self::getContainer()->getByType(RuleLevelHelper::class); + $this->initialCheckExplicitMixed = RuleLevelHelperHack::isCheckExplicitMixed($helper); + $this->initialCheckImplicitMixed = RuleLevelHelperHack::isCheckImplicitMixed($helper); + $this->initialCheckThisOnly = RuleLevelHelperHack::isCheckThisOnly($helper); + } + + protected function tearDown(): void + { + parent::tearDown(); + + RuleLevelHelperHack::setCheckExplicitMixed($this->tearDownRuleLevelHelper, $this->initialCheckExplicitMixed); + RuleLevelHelperHack::setCheckImplicitMixed($this->tearDownRuleLevelHelper, $this->initialCheckImplicitMixed); + RuleLevelHelperHack::setCheckThisOnly($this->tearDownRuleLevelHelper, $this->initialCheckThisOnly); + + unset($this->tearDownRuleLevelHelper); + } + /** @api */ public static function getContainer(): Container { @@ -191,6 +221,7 @@ public static function createScopeFactory(ReflectionProvider $reflectionProvider $container->getParameter('featureToggles')['explicitMixedInUnknownGenericNew'], $container->getParameter('featureToggles')['explicitMixedForGlobalVariables'], $constantResolver, + self::getContainer()->getByType(RuleLevelHelper::class), ), ); } diff --git a/src/Testing/RuleLevelHelperHack.php b/src/Testing/RuleLevelHelperHack.php new file mode 100644 index 0000000000..d875c40c04 --- /dev/null +++ b/src/Testing/RuleLevelHelperHack.php @@ -0,0 +1,40 @@ +checkExplicitMixed; + } + + public static function setCheckExplicitMixed(RuleLevelHelper $helper, bool $checkExplicitMixed): void + { + $helper->checkExplicitMixed = $checkExplicitMixed; + } + + public static function isCheckImplicitMixed(RuleLevelHelper $helper): bool + { + return $helper->checkImplicitMixed; + } + + public static function setCheckImplicitMixed(RuleLevelHelper $helper, bool $checkImplicitMixed): void + { + $helper->checkImplicitMixed = $checkImplicitMixed; + } + + public static function isCheckThisOnly(RuleLevelHelper $helper): bool + { + return $helper->checkThisOnly; + } + + public static function setCheckThisOnly(RuleLevelHelper $helper, bool $checkThisOnly): void + { + $helper->checkThisOnly = $checkThisOnly; + } + +} diff --git a/tests/PHPStan/Analyser/AnalyserTraitsIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserTraitsIntegrationTest.php index 023d00e9f4..f423d45380 100644 --- a/tests/PHPStan/Analyser/AnalyserTraitsIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserTraitsIntegrationTest.php @@ -18,6 +18,8 @@ class AnalyserTraitsIntegrationTest extends PHPStanTestCase protected function setUp(): void { + parent::setUp(); + $this->fileHelper = self::getContainer()->getByType(FileHelper::class); } diff --git a/tests/PHPStan/Analyser/TypeSpecifierTest.php b/tests/PHPStan/Analyser/TypeSpecifierTest.php index 245ba3d554..21c77610b7 100644 --- a/tests/PHPStan/Analyser/TypeSpecifierTest.php +++ b/tests/PHPStan/Analyser/TypeSpecifierTest.php @@ -57,6 +57,8 @@ class TypeSpecifierTest extends PHPStanTestCase protected function setUp(): void { + parent::setUp(); + $reflectionProvider = $this->createReflectionProvider(); $this->printer = new Printer(); $this->typeSpecifier = self::getContainer()->getService('typeSpecifier'); diff --git a/tests/PHPStan/Analyser/traitsCachingIssue/TraitsCachingIssueIntegrationTest.php b/tests/PHPStan/Analyser/traitsCachingIssue/TraitsCachingIssueIntegrationTest.php index ee7dd1f215..11869d8601 100644 --- a/tests/PHPStan/Analyser/traitsCachingIssue/TraitsCachingIssueIntegrationTest.php +++ b/tests/PHPStan/Analyser/traitsCachingIssue/TraitsCachingIssueIntegrationTest.php @@ -23,6 +23,8 @@ class TraitsCachingIssueIntegrationTest extends PHPStanTestCase public function tearDown(): void { + parent::tearDown(); + $this->deleteCache(); if ($this->originalTraitOneContents !== null) { diff --git a/tests/PHPStan/Command/ErrorFormatter/TableErrorFormatterTest.php b/tests/PHPStan/Command/ErrorFormatter/TableErrorFormatterTest.php index 5232f164ff..631ca0c28c 100644 --- a/tests/PHPStan/Command/ErrorFormatter/TableErrorFormatterTest.php +++ b/tests/PHPStan/Command/ErrorFormatter/TableErrorFormatterTest.php @@ -18,11 +18,15 @@ class TableErrorFormatterTest extends ErrorFormatterTestCase protected function setUp(): void { + parent::setUp(); + putenv('GITHUB_ACTIONS'); } protected function tearDown(): void { + parent::tearDown(); + putenv('COLUMNS'); } diff --git a/tests/PHPStan/PhpDoc/DefaultStubFilesProviderTest.php b/tests/PHPStan/PhpDoc/DefaultStubFilesProviderTest.php index e7ea48c599..83a372092a 100644 --- a/tests/PHPStan/PhpDoc/DefaultStubFilesProviderTest.php +++ b/tests/PHPStan/PhpDoc/DefaultStubFilesProviderTest.php @@ -12,6 +12,8 @@ class DefaultStubFilesProviderTest extends PHPStanTestCase protected function setUp(): void { + parent::setUp(); + $this->currentWorkingDirectory = $this->getContainer()->getParameter('currentWorkingDirectory'); } diff --git a/tests/PHPStan/Rules/Api/PhpStanNamespaceIn3rdPartyPackageRuleTest.php b/tests/PHPStan/Rules/Api/PhpStanNamespaceIn3rdPartyPackageRuleTest.php index 4c50e26ccc..fbf26fa675 100644 --- a/tests/PHPStan/Rules/Api/PhpStanNamespaceIn3rdPartyPackageRuleTest.php +++ b/tests/PHPStan/Rules/Api/PhpStanNamespaceIn3rdPartyPackageRuleTest.php @@ -21,6 +21,8 @@ protected function getRule(): Rule protected function tearDown(): void { + parent::tearDown(); + @unlink(__DIR__ . '/composer.json'); } diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index 670e24f5be..d80126a911 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -10,6 +10,7 @@ use PHPStan\Rules\Properties\PropertyReflectionFinder; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleLevelHelper; +use PHPStan\Testing\RuleLevelHelperHack; use PHPStan\Testing\RuleTestCase; use const PHP_VERSION_ID; @@ -23,10 +24,15 @@ class CallStaticMethodsRuleTest extends RuleTestCase private bool $checkExplicitMixed = false; + private bool $checkImplicitMixed = false; + protected function getRule(): Rule { + $ruleLevelHelper = self::getContainer()->getByType(RuleLevelHelper::class); + RuleLevelHelperHack::setCheckImplicitMixed($ruleLevelHelper, $this->checkImplicitMixed); + RuleLevelHelperHack::setCheckExplicitMixed($ruleLevelHelper, $this->checkExplicitMixed); + RuleLevelHelperHack::setCheckThisOnly($ruleLevelHelper, $this->checkThisOnly); $reflectionProvider = $this->createReflectionProvider(); - $ruleLevelHelper = new RuleLevelHelper($reflectionProvider, true, $this->checkThisOnly, true, $this->checkExplicitMixed, false, true, false); return new CallStaticMethodsRule( new StaticMethodCallCheck($reflectionProvider, $ruleLevelHelper, new ClassCaseSensitivityCheck($reflectionProvider, true), true, true), new FunctionCallParametersCheck($ruleLevelHelper, new NullsafeCheck(), new PhpVersion(80000), new UnresolvableTypeHelper(), new PropertyReflectionFinder(), true, true, true, true, true), @@ -629,4 +635,33 @@ public function testRequireImplements(): void ]); } + public function testGenericInstanceofEnum(): void + { + if (PHP_VERSION_ID < 80100) { + $this->markTestSkipped('Test requires PHP 8.1'); + } + + $this->checkThisOnly = false; + $this->checkExplicitMixed = true; + $this->checkImplicitMixed = true; + $this->analyse([__DIR__ . '/data/generic-instanceof-enum.php'], [ + [ + 'Call to an undefined static method T of mixed&UnitEnum::from().', + 14, + ], + [ + 'Call to an undefined static method T of mixed&UnitEnum::from().', + 25, + ], + [ + 'Call to an undefined static method T of object&UnitEnum::from().', + 36, + ], + [ + 'Call to an undefined static method UnitEnum::from().', + 43, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/generic-instanceof-enum.php b/tests/PHPStan/Rules/Methods/data/generic-instanceof-enum.php new file mode 100644 index 0000000000..186ff57b53 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/generic-instanceof-enum.php @@ -0,0 +1,46 @@ += 8.1 + +namespace GenericInstanceofEnum; + +class HelloWorld +{ + /** + * @template T + * @param T $val + */ + public function foo(mixed $val): void + { + if ($val instanceof \UnitEnum) { + $val::from('a'); + } + } + + /** + * @template T of mixed + * @param T $val + */ + public function foo2(mixed $val): void + { + if ($val instanceof \UnitEnum) { + $val::from('a'); + } + } + + /** + * @template T of object + * @param T $val + */ + public function foo3(mixed $val): void + { + if ($val instanceof \UnitEnum) { + $val::from('a'); + } + } + + public function foo4(mixed $val): void + { + if ($val instanceof \UnitEnum) { + $val::from('a'); + } + } +} From 528807a927a6d5f686fd192f72ea56fbed4b2ecb Mon Sep 17 00:00:00 2001 From: schlndh Date: Sat, 13 Jan 2024 14:08:28 +0100 Subject: [PATCH 2/3] allow specifying config for individual tests --- src/Testing/PHPStanTestCase.php | 56 ++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/src/Testing/PHPStanTestCase.php b/src/Testing/PHPStanTestCase.php index c6ba30178a..ca52d644c1 100644 --- a/src/Testing/PHPStanTestCase.php +++ b/src/Testing/PHPStanTestCase.php @@ -2,6 +2,7 @@ namespace PHPStan\Testing; +use Nette\Neon\Neon; use PHPStan\Analyser\ConstantResolver; use PHPStan\Analyser\DirectInternalScopeFactory; use PHPStan\Analyser\Error; @@ -40,11 +41,16 @@ use PHPUnit\Framework\TestCase; use function array_merge; use function count; +use function file_put_contents; use function implode; +use function is_array; +use function ksort; +use function rand; use function rtrim; use function sha1; use function sprintf; use function sys_get_temp_dir; +use function unlink; use const DIRECTORY_SEPARATOR; use const PHP_VERSION_ID; @@ -58,33 +64,28 @@ abstract class PHPStanTestCase extends TestCase /** @var array */ private static array $containers = []; - private RuleLevelHelper $tearDownRuleLevelHelper; + private static ?string $testCaseConfig = null; - private bool $initialCheckExplicitMixed; - - private bool $initialCheckImplicitMixed; - - private bool $initialCheckThisOnly; - - protected function setUp(): void + protected function tearDown(): void { - parent::setUp(); + parent::tearDown(); - $this->tearDownRuleLevelHelper = $helper = self::getContainer()->getByType(RuleLevelHelper::class); - $this->initialCheckExplicitMixed = RuleLevelHelperHack::isCheckExplicitMixed($helper); - $this->initialCheckImplicitMixed = RuleLevelHelperHack::isCheckImplicitMixed($helper); - $this->initialCheckThisOnly = RuleLevelHelperHack::isCheckThisOnly($helper); + self::$testCaseConfig = null; } - protected function tearDown(): void + /** @param array|null $config */ + protected function setTestCaseConfig(?array $config): void { - parent::tearDown(); - - RuleLevelHelperHack::setCheckExplicitMixed($this->tearDownRuleLevelHelper, $this->initialCheckExplicitMixed); - RuleLevelHelperHack::setCheckImplicitMixed($this->tearDownRuleLevelHelper, $this->initialCheckImplicitMixed); - RuleLevelHelperHack::setCheckThisOnly($this->tearDownRuleLevelHelper, $this->initialCheckThisOnly); + if ($config === null) { + self::$testCaseConfig = null; + return; + } + // Normalize the config to avoid creating unnecessary containers + if (isset($config['parameters']) && is_array($config['parameters'])) { + ksort($config['parameters']); + } - unset($this->tearDownRuleLevelHelper); + self::$testCaseConfig = Neon::encode($config); } /** @api */ @@ -92,7 +93,7 @@ public static function getContainer(): Container { $additionalConfigFiles = static::getAdditionalConfigFiles(); $additionalConfigFiles[] = __DIR__ . '/TestCase.neon'; - $cacheKey = sha1(implode("\n", $additionalConfigFiles)); + $cacheKey = sha1(implode("\n", $additionalConfigFiles) . self::$testCaseConfig); if (!isset(self::$containers[$cacheKey])) { $tmpDir = sys_get_temp_dir() . '/phpstan-tests'; @@ -102,6 +103,15 @@ public static function getContainer(): Container self::fail($e->getMessage()); } + $testCaseConfigFile = null; + if (self::$testCaseConfig !== null) { + $testCaseConfigFile = $tmpDir . '/test_case_config_' . rand() . '.neon'; + if (file_put_contents($testCaseConfigFile, self::$testCaseConfig) === false) { + self::fail('Failed to write test case config to temp file.'); + } + $additionalConfigFiles[] = $testCaseConfigFile; + } + $rootDir = __DIR__ . '/../..'; $fileHelper = new FileHelper($rootDir); $rootDir = $fileHelper->normalizePath($rootDir, '/'); @@ -124,6 +134,10 @@ public static function getContainer(): Container require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnumUnitCase.php'; require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnumBackedCase.php'; } + + if ($testCaseConfigFile !== null) { + unlink($testCaseConfigFile); + } } else { ContainerFactory::postInitializeContainer(self::$containers[$cacheKey]); } From 3a03551d47613aa723b264dd645aac0d8ab7e03c Mon Sep 17 00:00:00 2001 From: schlndh Date: Sat, 13 Jan 2024 14:42:28 +0100 Subject: [PATCH 3/3] replace RuleLevelHelperHack with setTestCaseConfig --- src/Rules/RuleLevelHelper.php | 6 +- src/Testing/RuleLevelHelperHack.php | 40 --------- .../Methods/CallStaticMethodsRuleTest.php | 83 ++++--------------- 3 files changed, 21 insertions(+), 108 deletions(-) delete mode 100644 src/Testing/RuleLevelHelperHack.php diff --git a/src/Rules/RuleLevelHelper.php b/src/Rules/RuleLevelHelper.php index 28076b6e7b..3ba9687ab3 100644 --- a/src/Rules/RuleLevelHelper.php +++ b/src/Rules/RuleLevelHelper.php @@ -33,10 +33,10 @@ class RuleLevelHelper public function __construct( private ReflectionProvider $reflectionProvider, private bool $checkNullables, - protected bool $checkThisOnly, + private bool $checkThisOnly, private bool $checkUnionTypes, - protected bool $checkExplicitMixed, - protected bool $checkImplicitMixed, + private bool $checkExplicitMixed, + private bool $checkImplicitMixed, private bool $newRuleLevelHelper, private bool $checkBenevolentUnionTypes, ) diff --git a/src/Testing/RuleLevelHelperHack.php b/src/Testing/RuleLevelHelperHack.php deleted file mode 100644 index d875c40c04..0000000000 --- a/src/Testing/RuleLevelHelperHack.php +++ /dev/null @@ -1,40 +0,0 @@ -checkExplicitMixed; - } - - public static function setCheckExplicitMixed(RuleLevelHelper $helper, bool $checkExplicitMixed): void - { - $helper->checkExplicitMixed = $checkExplicitMixed; - } - - public static function isCheckImplicitMixed(RuleLevelHelper $helper): bool - { - return $helper->checkImplicitMixed; - } - - public static function setCheckImplicitMixed(RuleLevelHelper $helper, bool $checkImplicitMixed): void - { - $helper->checkImplicitMixed = $checkImplicitMixed; - } - - public static function isCheckThisOnly(RuleLevelHelper $helper): bool - { - return $helper->checkThisOnly; - } - - public static function setCheckThisOnly(RuleLevelHelper $helper, bool $checkThisOnly): void - { - $helper->checkThisOnly = $checkThisOnly; - } - -} diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index d80126a911..6d00ef5032 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -10,7 +10,6 @@ use PHPStan\Rules\Properties\PropertyReflectionFinder; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleLevelHelper; -use PHPStan\Testing\RuleLevelHelperHack; use PHPStan\Testing\RuleTestCase; use const PHP_VERSION_ID; @@ -20,19 +19,11 @@ class CallStaticMethodsRuleTest extends RuleTestCase { - private bool $checkThisOnly; - - private bool $checkExplicitMixed = false; - - private bool $checkImplicitMixed = false; - protected function getRule(): Rule { - $ruleLevelHelper = self::getContainer()->getByType(RuleLevelHelper::class); - RuleLevelHelperHack::setCheckImplicitMixed($ruleLevelHelper, $this->checkImplicitMixed); - RuleLevelHelperHack::setCheckExplicitMixed($ruleLevelHelper, $this->checkExplicitMixed); - RuleLevelHelperHack::setCheckThisOnly($ruleLevelHelper, $this->checkThisOnly); $reflectionProvider = $this->createReflectionProvider(); + $ruleLevelHelper = $this->getContainer()->getByType(RuleLevelHelper::class); + return new CallStaticMethodsRule( new StaticMethodCallCheck($reflectionProvider, $ruleLevelHelper, new ClassCaseSensitivityCheck($reflectionProvider, true), true, true), new FunctionCallParametersCheck($ruleLevelHelper, new NullsafeCheck(), new PhpVersion(80000), new UnresolvableTypeHelper(), new PropertyReflectionFinder(), true, true, true, true, true), @@ -41,7 +32,6 @@ protected function getRule(): Rule public function testCallStaticMethods(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/call-static-methods.php'], [ [ 'Call to an undefined static method CallStaticMethods\Foo::bar().', @@ -238,7 +228,6 @@ public function testCallStaticMethods(): void public function testCallInterfaceMethods(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/call-interface-methods.php'], [ [ 'Cannot call abstract static method InterfaceMethods\Foo::fooStaticMethod().', @@ -253,7 +242,6 @@ public function testCallInterfaceMethods(): void public function testCallToIncorrectCaseMethodName(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/incorrect-static-method-case.php'], [ [ 'Call to static method IncorrectStaticMethodCase\Foo::fooBar() with incorrect case: foobar', @@ -264,7 +252,6 @@ public function testCallToIncorrectCaseMethodName(): void public function testStaticCallsToInstanceMethods(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/static-calls-to-instance-methods.php'], [ [ 'Static call to instance method StaticCallsToInstanceMethods\Foo::doFoo().', @@ -299,7 +286,6 @@ public function testStaticCallsToInstanceMethods(): void public function testStaticCallOnExpression(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/static-call-on-expression.php'], [ [ 'Call to an undefined static method StaticCallOnExpression\Foo::doBar().', @@ -310,13 +296,16 @@ public function testStaticCallOnExpression(): void public function testStaticCallOnExpressionWithCheckDisabled(): void { - $this->checkThisOnly = true; + $this->setTestCaseConfig([ + 'parameters' => [ + 'checkThisOnly' => true, + ], + ]); $this->analyse([__DIR__ . '/data/static-call-on-expression.php'], []); } public function testReturnStatic(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/return-static-static-method.php'], [ [ 'Call to an undefined static method ReturnStaticStaticMethod\Bar::doBaz().', @@ -327,7 +316,6 @@ public function testReturnStatic(): void public function testCallParentAbstractMethod(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/call-parent-abstract-method.php'], [ [ 'Cannot call abstract method CallParentAbstractMethod\Baz::uninstall().', @@ -350,13 +338,11 @@ public function testCallParentAbstractMethod(): void public function testClassExists(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/static-methods-class-exists.php'], []); } public function testBug3448(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-3448.php'], [ [ 'Parameter #1 $lall of static method Bug3448\Foo::add() expects int, string given.', @@ -371,7 +357,6 @@ public function testBug3448(): void public function testBug3641(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-3641.php'], [ [ 'Static method Bug3641\Foo::bar() invoked with 1 parameter, 0 required.', @@ -382,7 +367,6 @@ public function testBug3641(): void public function testBug2164(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-2164.php'], [ [ 'Parameter #1 $arg of static method Bug2164\A::staticTest() expects static(Bug2164\B)|string, Bug2164\B|string given.', @@ -393,8 +377,6 @@ public function testBug2164(): void public function testNamedArguments(): void { - $this->checkThisOnly = false; - $this->analyse([__DIR__ . '/data/static-method-named-arguments.php'], [ [ 'Missing parameter $j (int) in call to static method StaticMethodNamedArguments\Foo::doFoo().', @@ -409,13 +391,11 @@ public function testNamedArguments(): void public function testBug577(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-577.php'], []); } public function testBug4550(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-4550.php'], [ [ 'Parameter #1 $class of static method Bug4550\Test::valuesOf() expects class-string, string given.', @@ -434,7 +414,6 @@ public function testBug1971(): void $this->markTestSkipped('Test requires PHP 7.x'); } - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-1971.php'], [ [ 'Parameter #1 $callback of static method Closure::fromCallable() expects callable(): mixed, array{class-string, \'sayHello2\'} given.', @@ -449,7 +428,6 @@ public function testBug1971Php8(): void $this->markTestSkipped('Test requires PHP 8.0'); } - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-1971.php'], [ [ 'Parameter #1 $callback of static method Closure::fromCallable() expects callable(): mixed, array{\'Bug1971\\\HelloWorld\', \'sayHello\'} given.', @@ -468,63 +446,53 @@ public function testBug1971Php8(): void public function testBug5259(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-5259.php'], []); } public function testBug5536(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-5536.php'], []); } public function testBug4886(): void { - $this->checkThisOnly = false; $this->analyse([__DIR__ . '/data/bug-4886.php'], []); } public function testFirstClassCallables(): void { - $this->checkThisOnly = false; - // handled by a different rule $this->analyse([__DIR__ . '/data/first-class-static-method-callable.php'], []); } public function testBug5893(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/bug-5893.php'], []); } public function testBug6249(): void { // discussion https://github.com/phpstan/phpstan/discussions/6249 - $this->checkThisOnly = false; - $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/bug-6249.php'], []); } public function testBug5749(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/bug-5749.php'], []); } public function testBug5757(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/bug-5757.php'], []); } public function testDiscussion7004(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = true; + $this->setTestCaseConfig([ + 'parameters' => [ + 'checkExplicitMixed' => true, + ], + ]); $this->analyse([__DIR__ . '/data/discussion-7004.php'], [ [ 'Parameter #1 $data of static method Discussion7004\Foo::fromArray1() expects array, array given.', @@ -543,8 +511,6 @@ public function testDiscussion7004(): void public function testTemplateTypeInOneBranchOfConditional(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/template-type-in-one-branch-of-conditional.php'], [ [ 'Parameter #1 $params of static method TemplateTypeInOneBranchOfConditional\DriverManager::getConnection() expects array{wrapperClass?: class-string}, array{wrapperClass: \'stdClass\'} given.', @@ -561,15 +527,11 @@ public function testTemplateTypeInOneBranchOfConditional(): void public function testBug7489(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/bug-7489.php'], []); } public function testHasMethodStaticCall(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = false; $this->analyse([__DIR__ . '/data/static-has-method.php'], [ [ 'Call to an undefined static method StaticHasMethodCall\rex_var::doesNotExist().', @@ -584,22 +546,16 @@ public function testHasMethodStaticCall(): void public function testBug1267(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = false; $this->analyse([__DIR__ . '/data/bug-1267.php'], []); } public function testBug6147(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = false; $this->analyse([__DIR__ . '/data/bug-6147.php'], []); } public function testBug5781(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = false; $this->analyse([__DIR__ . '/data/bug-5781.php'], [ [ 'Parameter #1 $param of static method Bug5781\Foo::bar() expects array{a: bool, b: bool, c: bool, d: bool, e: bool, f: bool, g: bool, h: bool, ...}, array{} given.', @@ -611,9 +567,6 @@ public function testBug5781(): void public function testRequireExtends(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = false; - $this->analyse([__DIR__ . '/../Properties/data/require-extends.php'], [ [ 'Call to an undefined static method RequireExtends\MyInterface::doesNotExistStatic().', @@ -624,9 +577,6 @@ public function testRequireExtends(): void public function testRequireImplements(): void { - $this->checkThisOnly = false; - $this->checkExplicitMixed = false; - $this->analyse([__DIR__ . '/../Properties/data/require-implements.php'], [ [ 'Call to an undefined static method RequireImplements\MyBaseClass::doesNotExistStatic().', @@ -641,9 +591,12 @@ public function testGenericInstanceofEnum(): void $this->markTestSkipped('Test requires PHP 8.1'); } - $this->checkThisOnly = false; - $this->checkExplicitMixed = true; - $this->checkImplicitMixed = true; + $this->setTestCaseConfig([ + 'parameters' => [ + 'checkExplicitMixed' => true, + 'checkImplicitMixed' => true, + ], + ]); $this->analyse([__DIR__ . '/data/generic-instanceof-enum.php'], [ [ 'Call to an undefined static method T of mixed&UnitEnum::from().',