diff --git a/tools/.phpstan/composer.json b/tools/.phpstan/composer.json index 94d322a2a..364224b2b 100644 --- a/tools/.phpstan/composer.json +++ b/tools/.phpstan/composer.json @@ -2,7 +2,7 @@ "require-dev": { "phpstan/phpstan": "^2.3.5", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan-phpunit": "^2.1.1", + "phpstan/phpstan-phpunit": "^2.1.2", "phpstan/phpstan-strict-rules": "^2.1.1", "ergebnis/phpstan-rules": "^2.17.1", "phpstan/phpstan-deprecation-rules": "^2.0.5" diff --git a/tools/.phpstan/composer.lock b/tools/.phpstan/composer.lock index 791d6c810..a82519e01 100644 --- a/tools/.phpstan/composer.lock +++ b/tools/.phpstan/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": "64a7f71af8de3bd8f784f930657904be", + "content-hash": "36b5c4d648ebd338f4f96b0bd295baac", "packages": [], "packages-dev": [ { @@ -215,7 +215,8 @@ "issues": "https://github.com/phar-io/version/issues", "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2022-02-21T01:04:05+00:00" + "time": "2022-02-21T01:04:05+00:00", + "published-time": "2022-02-21T01:04:30+00:00" }, { "name": "phpstan/extension-installer", @@ -382,16 +383,16 @@ }, { "name": "phpstan/phpstan-phpunit", - "version": "2.1.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "8d16b8478e523a47f8c02e83bce8981b5b5a3b03" + "reference": "ffff35d4f96c4642d7f41a3515d90afa0f58be62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/8d16b8478e523a47f8c02e83bce8981b5b5a3b03", - "reference": "8d16b8478e523a47f8c02e83bce8981b5b5a3b03", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/ffff35d4f96c4642d7f41a3515d90afa0f58be62", + "reference": "ffff35d4f96c4642d7f41a3515d90afa0f58be62", "shasum": "" }, "require": { @@ -434,9 +435,10 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.1.1" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.1.2" }, - "time": "2026-10-06T18:12:32+00:00" + "time": "2026-11-12T15:27:53+00:00", + "published-time": "2026-11-12T16:07:51+00:00" }, { "name": "phpstan/phpstan-strict-rules", diff --git a/tools/.phpstan/vendor/composer/installed.json b/tools/.phpstan/vendor/composer/installed.json index f55f4367c..d4c0c9218 100644 --- a/tools/.phpstan/vendor/composer/installed.json +++ b/tools/.phpstan/vendor/composer/installed.json @@ -392,17 +392,17 @@ }, { "name": "phpstan/phpstan-phpunit", - "version": "2.1.1", - "version_normalized": "2.1.1.0", + "version": "2.1.2", + "version_normalized": "2.1.2.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "8d16b8478e523a47f8c02e83bce8981b5b5a3b03" + "reference": "ffff35d4f96c4642d7f41a3515d90afa0f58be62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/8d16b8478e523a47f8c02e83bce8981b5b5a3b03", - "reference": "8d16b8478e523a47f8c02e83bce8981b5b5a3b03", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/ffff35d4f96c4642d7f41a3515d90afa0f58be62", + "reference": "ffff35d4f96c4642d7f41a3515d90afa0f58be62", "shasum": "" }, "require": { @@ -421,7 +421,8 @@ "phpunit/phpunit": "^9.6", "shipmonk/name-collision-detector": "^2.1" }, - "time": "2026-10-06T18:12:32+00:00", + "time": "2026-11-12T15:27:53+00:00", + "published-time": "2026-11-12T16:07:51+00:00", "type": "phpstan-extension", "extra": { "phpstan": { @@ -447,7 +448,7 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.1.1" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.1.2" }, "install-path": "../phpstan/phpstan-phpunit" }, diff --git a/tools/.phpstan/vendor/composer/installed.php b/tools/.phpstan/vendor/composer/installed.php index 186385a53..1f6a91f49 100644 --- a/tools/.phpstan/vendor/composer/installed.php +++ b/tools/.phpstan/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => '__root__', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => 'edeb5f3bd974a675f9959cddc486b35f94b83be8', + 'reference' => '606b72595db2fa4cf232ae95354795e429fc4fdd', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -13,7 +13,7 @@ '__root__' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => 'edeb5f3bd974a675f9959cddc486b35f94b83be8', + 'reference' => '606b72595db2fa4cf232ae95354795e429fc4fdd', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -74,9 +74,9 @@ 'dev_requirement' => true, ), 'phpstan/phpstan-phpunit' => array( - 'pretty_version' => '2.1.1', - 'version' => '2.1.1.0', - 'reference' => '8d16b8478e523a47f8c02e83bce8981b5b5a3b03', + 'pretty_version' => '2.1.2', + 'version' => '2.1.2.0', + 'reference' => 'ffff35d4f96c4642d7f41a3515d90afa0f58be62', 'type' => 'phpstan-extension', 'install_path' => __DIR__ . '/../phpstan/phpstan-phpunit', 'aliases' => array(), diff --git a/tools/.phpstan/vendor/phpstan/phpstan-phpunit/rules.neon b/tools/.phpstan/vendor/phpstan/phpstan-phpunit/rules.neon index 6d4788d79..ce535a9b9 100644 --- a/tools/.phpstan/vendor/phpstan/phpstan-phpunit/rules.neon +++ b/tools/.phpstan/vendor/phpstan/phpstan-phpunit/rules.neon @@ -21,6 +21,9 @@ conditionalTags: PHPStan\Rules\PHPUnit\ClassAttributeRequiresPhpVersionRule: phpstan.rules.rule: %featureToggles.bleedingEdge% + PHPStan\Rules\PHPUnit\EmptyDataProviderRule: + phpstan.rules.rule: %featureToggles.bleedingEdge% + services: - class: PHPStan\Rules\PHPUnit\DataProviderDeclarationRule @@ -46,3 +49,6 @@ services: - class: PHPStan\Rules\PHPUnit\DataProviderDataRule + + - + class: PHPStan\Rules\PHPUnit\EmptyDataProviderRule diff --git a/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Rules/PHPUnit/EmptyDataProviderRule.php b/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Rules/PHPUnit/EmptyDataProviderRule.php new file mode 100644 index 000000000..a0511272b --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Rules/PHPUnit/EmptyDataProviderRule.php @@ -0,0 +1,109 @@ + + */ +class EmptyDataProviderRule implements Rule +{ + + private TestMethodsHelper $testMethodsHelper; + + private DataProviderHelper $dataProviderHelper; + + private PHPUnitVersion $PHPUnitVersion; + + public function __construct( + TestMethodsHelper $testMethodsHelper, + DataProviderHelper $dataProviderHelper, + PHPUnitVersion $PHPUnitVersion + ) + { + $this->testMethodsHelper = $testMethodsHelper; + $this->dataProviderHelper = $dataProviderHelper; + $this->PHPUnitVersion = $PHPUnitVersion; + } + + public function getNodeType(): string + { + return MethodReturnStatementsNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + // A generator yields its data sets one at a time, so whether it provides + // any depends on control flow this rule cannot decide. + if ($node->isGenerator()) { + return []; + } + + $returnStatements = $node->getReturnStatements(); + if ($returnStatements === []) { + return []; + } + + if ($this->PHPUnitVersion->rejectsEmptyDataProviders()->no()) { + return []; + } + + if (!$node->getClassReflection()->is(TestCase::class)) { + return []; + } + + foreach ($returnStatements as $returnStatement) { + $returnExpr = $returnStatement->getReturnNode()->expr; + if ($returnExpr === null) { + return []; + } + + $returnType = $returnStatement->getScope()->getType($returnExpr); + if (!$returnType->isIterable()->yes() || !$returnType->isIterableAtLeastOnce()->no()) { + return []; + } + } + + if (!$this->isDataProvider($node, $scope)) { + return []; + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Data provider method %s() provides no data sets, which is an error in PHPUnit 10 and newer.', + $node->getMethodName(), + )) + ->identifier('phpunit.dataProviderEmpty') + ->build(), + ]; + } + + private function isDataProvider(MethodReturnStatementsNode $node, Scope $scope): bool + { + $classReflection = $node->getClassReflection(); + + foreach ($this->testMethodsHelper->getTestMethods($classReflection, $scope) as $testMethod) { + foreach ($this->dataProviderHelper->getDataProviderMethods($scope, $testMethod, $classReflection) as [$providerClassReflection, $providerMethodName]) { + // A provider declared in another class is checked when that class is analysed. + if ($providerClassReflection === null || $providerClassReflection->getName() !== $classReflection->getName()) { + continue; + } + + if (strcasecmp($providerMethodName, $node->getMethodName()) === 0) { + return true; + } + } + } + + return false; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Rules/PHPUnit/PHPUnitVersion.php b/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Rules/PHPUnit/PHPUnitVersion.php index e6176b0af..0b6522b4b 100644 --- a/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Rules/PHPUnit/PHPUnitVersion.php +++ b/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Rules/PHPUnit/PHPUnitVersion.php @@ -58,6 +58,14 @@ public function requiresStaticDataProviders(): TrinaryLogic return TrinaryLogic::createFromBoolean($this->majorVersion >= 10); } + public function rejectsEmptyDataProviders(): TrinaryLogic + { + if ($this->majorVersion === null) { + return TrinaryLogic::createMaybe(); + } + return TrinaryLogic::createFromBoolean($this->majorVersion >= 10); + } + public function supportsNamedArgumentsInDataProvider(): TrinaryLogic { if ($this->majorVersion === null) { diff --git a/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertTypeSpecifyingExtensionHelper.php b/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertTypeSpecifyingExtensionHelper.php index 04def4e3c..8f8324035 100644 --- a/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertTypeSpecifyingExtensionHelper.php +++ b/tools/.phpstan/vendor/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertTypeSpecifyingExtensionHelper.php @@ -145,6 +145,14 @@ private static function getExpressionResolvers(): array new FuncCall(new Name('count'), [$actual]), ), ), + 'GreaterThan' => static fn (Scope $scope, Arg $expected, Arg $actual): Expr\BinaryOp\Greater => new Expr\BinaryOp\Greater( + $actual->value, + $expected->value, + ), + 'LessThan' => static fn (Scope $scope, Arg $expected, Arg $actual): Expr\BinaryOp\Smaller => new Expr\BinaryOp\Smaller( + $actual->value, + $expected->value, + ), 'InstanceOf' => static fn (Scope $scope, Arg $class, Arg $object): Instanceof_ => new Instanceof_( $object->value, $class->value,