diff --git a/tools/.phpstan/composer.json b/tools/.phpstan/composer.json index 4459bd379..a9a69f975 100644 --- a/tools/.phpstan/composer.json +++ b/tools/.phpstan/composer.json @@ -3,7 +3,7 @@ "phpstan/phpstan": "^2.1.56", "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan-strict-rules": "^2.0.11", - "tomasvotruba/type-coverage": "^2.1.0", + "tomasvotruba/type-coverage": "^2.2.1", "ergebnis/phpstan-rules": "^2.13.1" }, "config": { diff --git a/tools/.phpstan/composer.lock b/tools/.phpstan/composer.lock index fccc35216..25e54ecb1 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": "073573313dae16d84d9722d1e750a33d", + "content-hash": "a1a89a5081a183dea0929c9c7692b544", "packages": [], "packages-dev": [ { @@ -325,22 +325,22 @@ }, { "name": "tomasvotruba/type-coverage", - "version": "2.1.0", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/TomasVotruba/type-coverage.git", - "reference": "468354b3964120806a69890cbeb3fcf005876391" + "reference": "4087caa4639bdd4c646f2984bc333efeddf69e4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/468354b3964120806a69890cbeb3fcf005876391", - "reference": "468354b3964120806a69890cbeb3fcf005876391", + "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/4087caa4639bdd4c646f2984bc333efeddf69e4b", + "reference": "4087caa4639bdd4c646f2984bc333efeddf69e4b", "shasum": "" }, "require": { "nette/utils": "^3.2 || ^4.0", "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.0" + "phpstan/phpstan": "^2.1.33" }, "type": "phpstan-extension", "extra": { @@ -366,7 +366,7 @@ ], "support": { "issues": "https://github.com/TomasVotruba/type-coverage/issues", - "source": "https://github.com/TomasVotruba/type-coverage/tree/2.1.0" + "source": "https://github.com/TomasVotruba/type-coverage/tree/2.2.1" }, "funding": [ { @@ -378,7 +378,7 @@ "type": "github" } ], - "time": "2025-12-05T16:38:02+00:00" + "time": "2026-05-26T08:14:01+00:00" } ], "aliases": [], diff --git a/tools/.phpstan/vendor/composer/installed.json b/tools/.phpstan/vendor/composer/installed.json index c77be7153..6a58aeb49 100644 --- a/tools/.phpstan/vendor/composer/installed.json +++ b/tools/.phpstan/vendor/composer/installed.json @@ -333,25 +333,25 @@ }, { "name": "tomasvotruba/type-coverage", - "version": "2.1.0", - "version_normalized": "2.1.0.0", + "version": "2.2.1", + "version_normalized": "2.2.1.0", "source": { "type": "git", "url": "https://github.com/TomasVotruba/type-coverage.git", - "reference": "468354b3964120806a69890cbeb3fcf005876391" + "reference": "4087caa4639bdd4c646f2984bc333efeddf69e4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/468354b3964120806a69890cbeb3fcf005876391", - "reference": "468354b3964120806a69890cbeb3fcf005876391", + "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/4087caa4639bdd4c646f2984bc333efeddf69e4b", + "reference": "4087caa4639bdd4c646f2984bc333efeddf69e4b", "shasum": "" }, "require": { "nette/utils": "^3.2 || ^4.0", "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.0" + "phpstan/phpstan": "^2.1.33" }, - "time": "2025-12-05T16:38:02+00:00", + "time": "2026-05-26T08:14:01+00:00", "type": "phpstan-extension", "extra": { "phpstan": { @@ -377,7 +377,7 @@ ], "support": { "issues": "https://github.com/TomasVotruba/type-coverage/issues", - "source": "https://github.com/TomasVotruba/type-coverage/tree/2.1.0" + "source": "https://github.com/TomasVotruba/type-coverage/tree/2.2.1" }, "funding": [ { diff --git a/tools/.phpstan/vendor/composer/installed.php b/tools/.phpstan/vendor/composer/installed.php index 4983972a8..65214a030 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' => '65393074e5437e41b6f8d27247c19230c8ff02c7', + 'reference' => '0398c8400327a363191c3004854396b29281256e', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -13,7 +13,7 @@ '__root__' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '65393074e5437e41b6f8d27247c19230c8ff02c7', + 'reference' => '0398c8400327a363191c3004854396b29281256e', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -65,9 +65,9 @@ 'dev_requirement' => true, ), 'tomasvotruba/type-coverage' => array( - 'pretty_version' => '2.1.0', - 'version' => '2.1.0.0', - 'reference' => '468354b3964120806a69890cbeb3fcf005876391', + 'pretty_version' => '2.2.1', + 'version' => '2.2.1.0', + 'reference' => '4087caa4639bdd4c646f2984bc333efeddf69e4b', 'type' => 'phpstan-extension', 'install_path' => __DIR__ . '/../tomasvotruba/type-coverage', 'aliases' => array(), diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json index e32de7ffb..c3defb66d 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json @@ -6,7 +6,7 @@ "keywords": ["static analysis", "phpstan-extension"], "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.0", + "phpstan/phpstan": "^2.1.33", "nette/utils": "^3.2 || ^4.0" }, "autoload": { diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php index e05c09d15..78c13aa15 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php @@ -9,7 +9,7 @@ final class CollectorDataNormalizer { /** - * @param array}>> $collectorDataByPath + * @param array, 2?: string|null}>> $collectorDataByPath */ public function normalize(array $collectorDataByPath): TypeCountAndMissingTypes { @@ -24,8 +24,12 @@ public function normalize(array $collectorDataByPath): TypeCountAndMissingTypes $missingCount += count($nestedData[1]); - $missingTypeLinesByFilePath[$filePath] = array_merge( - $missingTypeLinesByFilePath[$filePath] ?? [], + // if the node is from a trait, route the error to the trait file + // instead of the using-class file, so lines match the actual source + $effectiveFilePath = $nestedData[2] ?? $filePath; + + $missingTypeLinesByFilePath[$effectiveFilePath] = array_merge( + $missingTypeLinesByFilePath[$effectiveFilePath] ?? [], $nestedData[1] ); } diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php index b0babdd80..c3ac810e3 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php @@ -26,13 +26,13 @@ public function getNodeType(): string /** * @param ClassConstantsNode $node - * @return array)>> + * @return array{int, list, string|null} */ public function processNode(Node $node, Scope $scope): array { // enable only on PHP 8.3+ if (PHP_VERSION_ID < 80300) { - return [0, []]; + return [0, [], null]; } $constantCount = count($node->getConstants()); @@ -54,7 +54,13 @@ public function processNode(Node $node, Scope $scope): array $missingTypeLines[] = $classConst->getLine(); } - return [$constantCount, $missingTypeLines]; + $traitFilePath = null; + if ($scope->isInTrait()) { + $traitFilePath = $scope->getTraitReflection() + ->getFileName(); + } + + return [$constantCount, $missingTypeLines, $traitFilePath]; } private function isGuardedByParentClassConstant(Scope $scope, ClassConst $classConst): bool diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php index a9d4a4943..c8f256714 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php @@ -7,8 +7,10 @@ use PhpParser\Comment\Doc; use PhpParser\Node; use PhpParser\Node\FunctionLike; +use PhpParser\Node\Stmt\ClassMethod; use PHPStan\Analyser\Scope; use PHPStan\Collectors\Collector; +use PHPStan\Reflection\ClassReflection; /** * @see \TomasVotruba\TypeCoverage\Rules\ParamTypeCoverageRule @@ -22,7 +24,7 @@ public function getNodeType(): string /** * @param FunctionLike $node - * @return mixed[]|null + * @return array{int, list, string|null}|null */ public function processNode(Node $node, Scope $scope): ?array { @@ -30,6 +32,11 @@ public function processNode(Node $node, Scope $scope): ?array return null; } + // skip methods inherited from a parent class or interface, as types are locked by LSP + if ($node instanceof ClassMethod && $this->isGuardedByParentMethod($scope, $node)) { + return null; + } + $missingTypeLines = []; $paramCount = count($node->getParams()); @@ -45,7 +52,17 @@ public function processNode(Node $node, Scope $scope): ?array } } - return [$paramCount, $missingTypeLines]; + return [$paramCount, $missingTypeLines, $this->resolveTraitFilePath($scope)]; + } + + private function resolveTraitFilePath(Scope $scope): ?string + { + if (! $scope->isInTrait()) { + return null; + } + + return $scope->getTraitReflection() + ->getFileName(); } private function shouldSkipFunctionLike(FunctionLike $functionLike): bool @@ -58,6 +75,30 @@ private function shouldSkipFunctionLike(FunctionLike $functionLike): bool return $this->hasFunctionLikeCallableParam($functionLike); } + private function isGuardedByParentMethod(Scope $scope, ClassMethod $classMethod): bool + { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + $methodName = $classMethod->name->toString(); + + foreach ($classReflection->getParents() as $parentClassReflection) { + if ($parentClassReflection->hasMethod($methodName)) { + return true; + } + } + + foreach ($classReflection->getInterfaces() as $interfaceReflection) { + if ($interfaceReflection->hasMethod($methodName)) { + return true; + } + } + + return false; + } + private function hasFunctionLikeCallableParam(FunctionLike $functionLike): bool { // skip callable, can be anythings diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php index 86c09ccde..412cd4279 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php @@ -27,7 +27,7 @@ public function getNodeType(): string /** * @param InClassNode $node - * @return array)>> + * @return array{int, list, string|null} */ public function processNode(Node $node, Scope $scope): array { @@ -57,7 +57,13 @@ public function processNode(Node $node, Scope $scope): array $missingTypeLines[] = $property->getLine(); } - return [$propertyCount, $missingTypeLines]; + $traitFilePath = null; + if ($scope->isInTrait()) { + $traitFilePath = $scope->getTraitReflection() + ->getFileName(); + } + + return [$propertyCount, $missingTypeLines, $traitFilePath]; } private function isPropertyDocTyped(Property $property): bool diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php index f6f63b841..e27ef1cdf 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php @@ -21,7 +21,7 @@ public function getNodeType(): string /** * @param ClassMethod $node - * @return mixed[]|null + * @return array{int, list, string|null}|null */ public function processNode(Node $node, Scope $scope): ?array { @@ -30,11 +30,15 @@ public function processNode(Node $node, Scope $scope): ?array return null; } + $traitFilePath = null; if ($scope->isInTrait()) { $originalMethodName = $node->getAttribute('originalTraitMethodName'); if ($originalMethodName === '__construct') { return null; } + + $traitFilePath = $scope->getTraitReflection() + ->getFileName(); } $missingTypeLines = []; @@ -43,6 +47,6 @@ public function processNode(Node $node, Scope $scope): ?array $missingTypeLines[] = $node->getLine(); } - return [1, $missingTypeLines]; + return [1, $missingTypeLines, $traitFilePath]; } } diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php index 314c0b597..c051aa48f 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php @@ -26,6 +26,11 @@ final class DeclareCoverageRule implements Rule */ public const ERROR_MESSAGE = 'Out of %d possible declare(strict_types=1), only %d - %.1f %% actually have it. Add more declares to get over %s %%'; + /** + * @var string + */ + private const IDENTIFIER = 'typeCoverage.declareCoverage'; + /** * @readonly */ @@ -108,7 +113,10 @@ public function processNode(Node $node, Scope $scope): array $requiredDeclareLevel, ); - $ruleErrors[] = RuleErrorBuilder::message($errorMessage)->file($notCoveredDeclareFilePath)->build(); + $ruleErrors[] = RuleErrorBuilder::message($errorMessage) + ->identifier(self::IDENTIFIER) + ->file($notCoveredDeclareFilePath) + ->build(); } return $ruleErrors;