From 937409d77aa6e7850b09bdb25e7abc53589904fd Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 08:51:56 +0100 Subject: [PATCH 01/22] Update phpstan/phpstan related dependencies to ^2.0" --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 8e3152e3..ac7d1f7f 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,8 @@ ], "require": { "php": "^8.1", - "phpstan/phpstan": "^1.10.56", - "phpstan/phpstan-deprecation-rules": "^1.1.4", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", "symfony/finder": "^4.2 || ^5.0 || ^6.0 || ^7.0", "symfony/yaml": "^4.2|| ^5.0 || ^6.0 || ^7.0", "webflo/drupal-finder": "^1.3.1" @@ -22,8 +22,8 @@ "composer/installers": "^1.9", "drupal/core-recommended": "^10", "drush/drush": "^10.0 || ^11 || ^12 || ^13@beta", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", + "phpstan/extension-installer": "1.4.3", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^8.5 || ^9 || ^10 || ^11", "slevomat/coding-standard": "^7.1", "squizlabs/php_codesniffer": "^3.3", From 7c58cbb94f15cf8b6aefa0c63ee99b0c1f88753f Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 09:00:48 +0100 Subject: [PATCH 02/22] Other versions now being also 2.0 until the next minor bump --- .github/workflows/phpstan-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/phpstan-dev.yml b/.github/workflows/phpstan-dev.yml index 15cd5c31..b136efa8 100644 --- a/.github/workflows/phpstan-dev.yml +++ b/.github/workflows/phpstan-dev.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: phpstan: - - '1.11.x-dev' + - '2.0.x-dev' steps: - name: "Checkout" uses: "actions/checkout@v4" From 4fe0b19101e20c1745f0c5532a744e51a7dd3a3f Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 18:08:31 +0100 Subject: [PATCH 03/22] PHPStan errors, low hanging fruit edition - Part I --- src/Drupal/DrupalAutoloader.php | 2 -- tests/src/Rules/GetDeprecatedServiceRuleTest.php | 4 ++++ .../GlobalDrupalDependencyInjectionRuleTest.php | 12 ++++++------ tests/src/Rules/LoadIncludesRuleTest.php | 2 ++ .../PluginAnnotationContextDefinitionsRuleTest.php | 2 ++ tests/src/Rules/PluginManagerInspectionRuleTest.php | 2 ++ .../Rules/PluginManagerSetsCacheBackendRuleTest.php | 2 ++ tests/src/Rules/RenderCallbackRuleTest.php | 2 ++ .../Rules/RevisionableStorageInterfaceStubTest.php | 1 + tests/src/Rules/TestClassSuffixNameRuleTest.php | 2 ++ .../TestClassesProtectedPropertyModulesRuleTest.php | 2 ++ tests/src/StubTest.php | 12 ++++++------ 12 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/Drupal/DrupalAutoloader.php b/src/Drupal/DrupalAutoloader.php index 760ca492..9d359d3e 100644 --- a/src/Drupal/DrupalAutoloader.php +++ b/src/Drupal/DrupalAutoloader.php @@ -193,7 +193,6 @@ public function register(Container $container): void $levels = 3; } $drushDir = dirname($reflect->getFileName(), $levels); - /** @var \SplFileInfo $file */ foreach (Finder::create()->files()->name('*.inc')->in($drushDir . '/includes') as $file) { require_once $file->getPathname(); } @@ -254,7 +253,6 @@ class: Drupal\jsonapi\Routing\JsonApiParamEnhancer protected function loadLegacyIncludes(): void { - /** @var \SplFileInfo $file */ foreach (Finder::create()->files()->name('*.inc')->in($this->drupalRoot . '/core/includes') as $file) { require_once $file->getPathname(); } diff --git a/tests/src/Rules/GetDeprecatedServiceRuleTest.php b/tests/src/Rules/GetDeprecatedServiceRuleTest.php index 05eb0655..4c7c82d9 100644 --- a/tests/src/Rules/GetDeprecatedServiceRuleTest.php +++ b/tests/src/Rules/GetDeprecatedServiceRuleTest.php @@ -17,6 +17,8 @@ protected function getRule(): \PHPStan\Rules\Rule /** * @dataProvider drupal8Data + * + * @param list $errorMessages */ public function testRuleDrupal8(string $path, array $errorMessages): void { @@ -28,6 +30,8 @@ public function testRuleDrupal8(string $path, array $errorMessages): void /** * @dataProvider drupal9Data + * + * @param list $errorMessages */ public function testRuleDrupal9(string $path, array $errorMessages): void { diff --git a/tests/src/Rules/GlobalDrupalDependencyInjectionRuleTest.php b/tests/src/Rules/GlobalDrupalDependencyInjectionRuleTest.php index 9ed76ab9..772ed467 100644 --- a/tests/src/Rules/GlobalDrupalDependencyInjectionRuleTest.php +++ b/tests/src/Rules/GlobalDrupalDependencyInjectionRuleTest.php @@ -14,6 +14,8 @@ protected function getRule(): \PHPStan\Rules\Rule /** * @dataProvider resultData + * + * @param list $errorMessages */ public function testRule(string $path, array $errorMessages): void { @@ -63,12 +65,10 @@ public static function resultData(): \Generator [], ]; - if (PHP_VERSION_ID >= 80100) { - yield [ - __DIR__ . '/data/bug-500.php', - [], - ]; - } + yield [ + __DIR__ . '/data/bug-500.php', + [], + ]; } diff --git a/tests/src/Rules/LoadIncludesRuleTest.php b/tests/src/Rules/LoadIncludesRuleTest.php index 95402b5c..2323c47d 100644 --- a/tests/src/Rules/LoadIncludesRuleTest.php +++ b/tests/src/Rules/LoadIncludesRuleTest.php @@ -14,6 +14,8 @@ protected function getRule(): \PHPStan\Rules\Rule /** * @dataProvider cases + * + * @param list $errors */ public function test(array $files, array $errors): void { diff --git a/tests/src/Rules/PluginAnnotationContextDefinitionsRuleTest.php b/tests/src/Rules/PluginAnnotationContextDefinitionsRuleTest.php index b0fcc694..12203699 100644 --- a/tests/src/Rules/PluginAnnotationContextDefinitionsRuleTest.php +++ b/tests/src/Rules/PluginAnnotationContextDefinitionsRuleTest.php @@ -16,6 +16,8 @@ protected function getRule(): \PHPStan\Rules\Rule /** * @dataProvider pluginData + * + * @param list $errorMessages */ public function testContextAnnotationRuleCheck(string $path, array $errorMessages): void { diff --git a/tests/src/Rules/PluginManagerInspectionRuleTest.php b/tests/src/Rules/PluginManagerInspectionRuleTest.php index fb09d234..f2725b9a 100644 --- a/tests/src/Rules/PluginManagerInspectionRuleTest.php +++ b/tests/src/Rules/PluginManagerInspectionRuleTest.php @@ -18,6 +18,8 @@ protected function getRule(): Rule /** * @dataProvider pluginManagerData + * + * @param list $errorMessages */ public function testRule(string $path, array $errorMessages): void { diff --git a/tests/src/Rules/PluginManagerSetsCacheBackendRuleTest.php b/tests/src/Rules/PluginManagerSetsCacheBackendRuleTest.php index ac2d97a1..888f3731 100644 --- a/tests/src/Rules/PluginManagerSetsCacheBackendRuleTest.php +++ b/tests/src/Rules/PluginManagerSetsCacheBackendRuleTest.php @@ -18,6 +18,8 @@ protected function getRule(): Rule /** * @dataProvider ruleData + * + * @param list $errorMessages */ public function testRule(string $path, array $errorMessages): void { diff --git a/tests/src/Rules/RenderCallbackRuleTest.php b/tests/src/Rules/RenderCallbackRuleTest.php index 435c58ba..40b33268 100644 --- a/tests/src/Rules/RenderCallbackRuleTest.php +++ b/tests/src/Rules/RenderCallbackRuleTest.php @@ -20,6 +20,8 @@ protected function getRule(): Rule /** * @dataProvider fileData + * + * @param list $errorMessages */ public function testRule(string $path, array $errorMessages): void { diff --git a/tests/src/Rules/RevisionableStorageInterfaceStubTest.php b/tests/src/Rules/RevisionableStorageInterfaceStubTest.php index 2d60e658..1141493f 100644 --- a/tests/src/Rules/RevisionableStorageInterfaceStubTest.php +++ b/tests/src/Rules/RevisionableStorageInterfaceStubTest.php @@ -17,6 +17,7 @@ protected function getRule(): Rule // @phpstan-ignore-next-line return new CallToDeprecatedMethodRule( self::createReflectionProvider(), + /** @phpstan-ignore phpstanApi.classConstant */ self::getContainer()->getByType(DeprecatedScopeHelper::class) ); } diff --git a/tests/src/Rules/TestClassSuffixNameRuleTest.php b/tests/src/Rules/TestClassSuffixNameRuleTest.php index c2158787..6464a16f 100644 --- a/tests/src/Rules/TestClassSuffixNameRuleTest.php +++ b/tests/src/Rules/TestClassSuffixNameRuleTest.php @@ -18,6 +18,8 @@ protected function getRule(): Rule /** * @dataProvider fileData + * + * @param list $errorMessages */ public function testRule(string $path, array $errorMessages): void { diff --git a/tests/src/Rules/TestClassesProtectedPropertyModulesRuleTest.php b/tests/src/Rules/TestClassesProtectedPropertyModulesRuleTest.php index 78bc5d84..e0a0203b 100644 --- a/tests/src/Rules/TestClassesProtectedPropertyModulesRuleTest.php +++ b/tests/src/Rules/TestClassesProtectedPropertyModulesRuleTest.php @@ -18,6 +18,8 @@ protected function getRule(): Rule /** * @dataProvider cases + * + * @param list $errors */ public function test(array $files, array $errors): void { diff --git a/tests/src/StubTest.php b/tests/src/StubTest.php index 52182b9a..44a2955d 100644 --- a/tests/src/StubTest.php +++ b/tests/src/StubTest.php @@ -14,14 +14,14 @@ final class StubTest extends PHPStanTestCase use AdditionalConfigFilesTrait; public function testValid(): void { - $stubFilesProvider = self::getContainer() - ->getByType(StubFilesProvider::class); - // @phpstan-ignore-next-line + /** @phpstan-ignore phpstanApi.classConstant */ + $stubFilesProvider = self::getContainer()->getByType(StubFilesProvider::class); + /** @phpstan-ignore phpstanApi.method */ $projectStubFiles = $stubFilesProvider->getProjectStubFiles(); - $stubValidators = self::getContainer() - ->getByType(StubValidator::class); - // @phpstan-ignore-next-line + /** @phpstan-ignore phpstanApi.classConstant */ + $stubValidators = self::getContainer()->getByType(StubValidator::class); + /** @phpstan-ignore phpstanApi.method */ $stubErrors = $stubValidators->validate($projectStubFiles, true); $errorsAsArrays = array_map( static fn (Error $error) => $error->jsonSerialize(), From 1d4464af6272389d5b2a210fa2606532cf700d55 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 19:29:31 +0100 Subject: [PATCH 04/22] PHPStan errors, low hanging fruit edition - Part II --- src/Rules/Drupal/RequestStackGetMainRequestRule.php | 1 + .../TestClassesProtectedPropertyModulesRule.php | 4 +++- .../Drupal/Tests/BrowserTestBaseDefaultThemeRule.php | 4 +++- src/Rules/Drupal/Tests/TestClassSuffixNameRule.php | 1 + .../AccessCheckTypeSpecifyingExtension.php | 2 +- src/Type/EntityRepositoryReturnTypeExtension.php | 2 +- .../DeprecatedScope/DeprecationHelperScopeTest.php | 3 ++- tests/src/DeprecatedScope/GlobalLegacyScopeTest.php | 3 ++- .../DeprecatedScope/IgnoreDeprecationsScopeTest.php | 3 ++- tests/src/DrupalAutoloadingTest.php | 2 ++ .../Rules/BrowserTestBaseDefaultThemeRuleTest.php | 2 ++ .../src/Rules/ClassExtendsInternalClassRuleTest.php | 2 ++ tests/src/Rules/ConfigEntityConfigExportRuleTest.php | 2 ++ tests/src/Rules/EntityParameterTypehintRuleTest.php | 12 ++++-------- .../src/Rules/EntityQueryHasAccessCheckRuleTest.php | 2 ++ .../Rules/RevisionableStorageInterfaceStubTest.php | 2 +- 16 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/Rules/Drupal/RequestStackGetMainRequestRule.php b/src/Rules/Drupal/RequestStackGetMainRequestRule.php index b4f78665..2f8d9734 100644 --- a/src/Rules/Drupal/RequestStackGetMainRequestRule.php +++ b/src/Rules/Drupal/RequestStackGetMainRequestRule.php @@ -52,6 +52,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message($message) ->tip('Change record: https://www.drupal.org/node/3253744') + ->identifier('deprecated.getMasterRequest') ->build(), ]; } diff --git a/src/Rules/Drupal/TestClassesProtectedPropertyModulesRule.php b/src/Rules/Drupal/TestClassesProtectedPropertyModulesRule.php index 8194b327..f5afaca3 100644 --- a/src/Rules/Drupal/TestClassesProtectedPropertyModulesRule.php +++ b/src/Rules/Drupal/TestClassesProtectedPropertyModulesRule.php @@ -46,7 +46,9 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message( sprintf('Property %s::$modules property must be protected.', $scopeClassReflection->getDisplayName()) - )->tip('Change record: https://www.drupal.org/node/2909426')->build(), + )->tip('Change record: https://www.drupal.org/node/2909426') + ->identifier('testClass.propertyModulesNonProtected') + ->build(), ]; } diff --git a/src/Rules/Drupal/Tests/BrowserTestBaseDefaultThemeRule.php b/src/Rules/Drupal/Tests/BrowserTestBaseDefaultThemeRule.php index 29648c2d..08c8dd50 100644 --- a/src/Rules/Drupal/Tests/BrowserTestBaseDefaultThemeRule.php +++ b/src/Rules/Drupal/Tests/BrowserTestBaseDefaultThemeRule.php @@ -104,7 +104,9 @@ public function processNode(Node $node, Scope $scope): array if ($defaultTheme === null || $defaultTheme === '') { return [ RuleErrorBuilder::message('Drupal\Tests\BrowserTestBase::$defaultTheme is required. See https://www.drupal.org/node/3083055, which includes recommendations on which theme to use.') - ->line($node->getStartLine())->build(), + ->line($node->getStartLine()) + ->identifier('BrowserTestBase.defaultThemeRequired') + ->build(), ]; } return []; diff --git a/src/Rules/Drupal/Tests/TestClassSuffixNameRule.php b/src/Rules/Drupal/Tests/TestClassSuffixNameRule.php index 6f8b83ea..8312c061 100644 --- a/src/Rules/Drupal/Tests/TestClassSuffixNameRule.php +++ b/src/Rules/Drupal/Tests/TestClassSuffixNameRule.php @@ -63,6 +63,7 @@ public function processNode(Node $node, Scope $scope): array ) ->line($node->getStartLine()) ->tip('See https://www.drupal.org/docs/develop/standards/php/object-oriented-code#naming') + ->identifier('nonAbstractTestClass.NameNoSuffixTest') ->build() ]; } diff --git a/src/Type/EntityQuery/AccessCheckTypeSpecifyingExtension.php b/src/Type/EntityQuery/AccessCheckTypeSpecifyingExtension.php index 5d777095..3d671be0 100644 --- a/src/Type/EntityQuery/AccessCheckTypeSpecifyingExtension.php +++ b/src/Type/EntityQuery/AccessCheckTypeSpecifyingExtension.php @@ -53,7 +53,7 @@ public function specifyTypes( $expr, $returnType->withAccessCheck(), TypeSpecifierContext::createTruthy(), - true + $scope ); } } diff --git a/src/Type/EntityRepositoryReturnTypeExtension.php b/src/Type/EntityRepositoryReturnTypeExtension.php index b290039c..02e74600 100644 --- a/src/Type/EntityRepositoryReturnTypeExtension.php +++ b/src/Type/EntityRepositoryReturnTypeExtension.php @@ -58,7 +58,7 @@ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): ?Type { + ): Type { $methodName = $methodReflection->getName(); $methodArgs = $methodCall->getArgs(); $returnType = ParametersAcceptorSelector::selectFromArgs( diff --git a/tests/src/DeprecatedScope/DeprecationHelperScopeTest.php b/tests/src/DeprecatedScope/DeprecationHelperScopeTest.php index 10569512..1afcb547 100644 --- a/tests/src/DeprecatedScope/DeprecationHelperScopeTest.php +++ b/tests/src/DeprecatedScope/DeprecationHelperScopeTest.php @@ -13,9 +13,10 @@ final class DeprecationHelperScopeTest extends DrupalRuleTestCase { protected function getRule(): Rule { - // @phpstan-ignore-next-line + /** @phpstan-ignore phpstanApi.constructor */ return new CallToDeprecatedFunctionRule( self::createReflectionProvider(), + /** @phpstan-ignore phpstanApi.classConstant */ self::getContainer()->getByType(DeprecatedScopeHelper::class) ); } diff --git a/tests/src/DeprecatedScope/GlobalLegacyScopeTest.php b/tests/src/DeprecatedScope/GlobalLegacyScopeTest.php index e74b16d4..de006acd 100644 --- a/tests/src/DeprecatedScope/GlobalLegacyScopeTest.php +++ b/tests/src/DeprecatedScope/GlobalLegacyScopeTest.php @@ -13,9 +13,10 @@ final class GlobalLegacyScopeTest extends DrupalRuleTestCase { protected function getRule(): Rule { - // @phpstan-ignore-next-line + /** @phpstan-ignore phpstanApi.constructor */ return new CallToDeprecatedFunctionRule( self::createReflectionProvider(), + /** @phpstan-ignore phpstanApi.classConstant */ self::getContainer()->getByType(DeprecatedScopeHelper::class) ); } diff --git a/tests/src/DeprecatedScope/IgnoreDeprecationsScopeTest.php b/tests/src/DeprecatedScope/IgnoreDeprecationsScopeTest.php index 2439fa6f..9657a84d 100644 --- a/tests/src/DeprecatedScope/IgnoreDeprecationsScopeTest.php +++ b/tests/src/DeprecatedScope/IgnoreDeprecationsScopeTest.php @@ -14,9 +14,10 @@ final class IgnoreDeprecationsScopeTest extends DrupalRuleTestCase { protected function getRule(): Rule { - // @phpstan-ignore-next-line + /** @phpstan-ignore phpstanApi.constructor */ return new CallToDeprecatedFunctionRule( self::createReflectionProvider(), + /** @phpstan-ignore phpstanApi.classConstant */ self::getContainer()->getByType(DeprecatedScopeHelper::class) ); } diff --git a/tests/src/DrupalAutoloadingTest.php b/tests/src/DrupalAutoloadingTest.php index c3d37f14..93e23dc3 100644 --- a/tests/src/DrupalAutoloadingTest.php +++ b/tests/src/DrupalAutoloadingTest.php @@ -14,6 +14,8 @@ protected function getRule(): \PHPStan\Rules\Rule /** * @dataProvider dataFixtures + * + * @param list $errorMessages */ public function testAnalyze(array $paths, array $errorMessages): void { diff --git a/tests/src/Rules/BrowserTestBaseDefaultThemeRuleTest.php b/tests/src/Rules/BrowserTestBaseDefaultThemeRuleTest.php index 994eae9f..1d243383 100644 --- a/tests/src/Rules/BrowserTestBaseDefaultThemeRuleTest.php +++ b/tests/src/Rules/BrowserTestBaseDefaultThemeRuleTest.php @@ -17,6 +17,8 @@ protected function getRule(): \PHPStan\Rules\Rule /** * @dataProvider fileData + * + * @param list $errorMessages */ public function testRule(string $path, array $errorMessages): void { diff --git a/tests/src/Rules/ClassExtendsInternalClassRuleTest.php b/tests/src/Rules/ClassExtendsInternalClassRuleTest.php index b17ef8d5..e634a897 100644 --- a/tests/src/Rules/ClassExtendsInternalClassRuleTest.php +++ b/tests/src/Rules/ClassExtendsInternalClassRuleTest.php @@ -38,6 +38,8 @@ public static function tearDownAfterClass(): void /** * @dataProvider pluginData + * + * @param list $errorMessages */ public function testRule(string $path, array $errorMessages): void { diff --git a/tests/src/Rules/ConfigEntityConfigExportRuleTest.php b/tests/src/Rules/ConfigEntityConfigExportRuleTest.php index 184fac53..7177caab 100644 --- a/tests/src/Rules/ConfigEntityConfigExportRuleTest.php +++ b/tests/src/Rules/ConfigEntityConfigExportRuleTest.php @@ -16,6 +16,8 @@ protected function getRule(): \PHPStan\Rules\Rule /** * @dataProvider pluginData + * + * @param list $errorMessages */ public function testConfigExportRuleCheck(string $path, array $errorMessages): void { diff --git a/tests/src/Rules/EntityParameterTypehintRuleTest.php b/tests/src/Rules/EntityParameterTypehintRuleTest.php index 76bfd6af..f246381d 100644 --- a/tests/src/Rules/EntityParameterTypehintRuleTest.php +++ b/tests/src/Rules/EntityParameterTypehintRuleTest.php @@ -11,17 +11,13 @@ final class EntityParameterTypehintRuleTest extends DrupalRuleTestCase { protected function getRule(): Rule { - // @phpstan-ignore-next-line + /** @phpstan-ignore phpstanApi.constructor */ return new MissingFunctionParameterTypehintRule( - // @phpstan-ignore-next-line - new MissingTypehintCheck( - true, - true, - true, + /** @phpstan-ignore phpstanApi.constructor */ + new MissingTypehintCheck( true, [] - ), - true + ) ); } diff --git a/tests/src/Rules/EntityQueryHasAccessCheckRuleTest.php b/tests/src/Rules/EntityQueryHasAccessCheckRuleTest.php index 3d61d7d0..6c8a5510 100644 --- a/tests/src/Rules/EntityQueryHasAccessCheckRuleTest.php +++ b/tests/src/Rules/EntityQueryHasAccessCheckRuleTest.php @@ -16,6 +16,8 @@ protected function getRule(): \PHPStan\Rules\Rule /** * @dataProvider cases + * + * @param list $errors */ public function test(array $files, array $errors): void { diff --git a/tests/src/Rules/RevisionableStorageInterfaceStubTest.php b/tests/src/Rules/RevisionableStorageInterfaceStubTest.php index 1141493f..41a61981 100644 --- a/tests/src/Rules/RevisionableStorageInterfaceStubTest.php +++ b/tests/src/Rules/RevisionableStorageInterfaceStubTest.php @@ -14,7 +14,7 @@ final class RevisionableStorageInterfaceStubTest extends DrupalRuleTestCase protected function getRule(): Rule { - // @phpstan-ignore-next-line + /** @phpstan-ignore phpstanApi.constructor */ return new CallToDeprecatedMethodRule( self::createReflectionProvider(), /** @phpstan-ignore phpstanApi.classConstant */ From 86d1191c0d5739792a6166b95ba5592e7695af2b Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 20:04:50 +0100 Subject: [PATCH 05/22] Big one... --- src/Rules/Drupal/RenderCallbackRule.php | 52 ++++++++++++++++++------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/Rules/Drupal/RenderCallbackRule.php b/src/Rules/Drupal/RenderCallbackRule.php index 413d943e..776cab39 100644 --- a/src/Rules/Drupal/RenderCallbackRule.php +++ b/src/Rules/Drupal/RenderCallbackRule.php @@ -9,6 +9,7 @@ use Drupal\Core\Security\TrustedCallbackInterface; use mglaman\PHPStanDrupal\Drupal\ServiceMap; use PhpParser\Node; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Name; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; @@ -63,7 +64,7 @@ public function __construct(ReflectionProvider $reflectionProvider, ServiceMap $ public function getNodeType(): string { - return Node\Expr\ArrayItem::class; + return ArrayItem::class; } public function processNode(Node $node, Scope $scope): array @@ -106,7 +107,9 @@ public function processNode(Node $node, Scope $scope): array if (!$value instanceof Node\Expr\Array_) { return [ RuleErrorBuilder::message(sprintf('The "%s" expects a callable array with arguments.', $keyChecked)) - ->line($node->getStartLine())->build() + ->line($node->getStartLine()) + ->identifier('renderCallback.expectsCallableArrayWithArguments') + ->build() ]; } if (count($value->items) === 0) { @@ -119,7 +122,9 @@ public function processNode(Node $node, Scope $scope): array if (!$value instanceof Node\Expr\Array_) { return [ RuleErrorBuilder::message(sprintf('The "%s" render array value expects an array of callbacks.', $keyChecked)) - ->line($node->getStartLine())->build() + ->line($node->getStartLine()) + ->identifier('renderCallback.expectsArrayOfCallbacks') + ->build() ]; } if (count($value->items) === 0) { @@ -133,7 +138,7 @@ public function processNode(Node $node, Scope $scope): array } /** - @return (string|\PHPStan\Rules\RuleError)[] errors + * @return list<\PHPStan\Rules\IdentifierRuleError> errors */ private function doProcessNode(Node\Expr $node, Scope $scope, string $keyChecked, int $pos): array { @@ -152,26 +157,33 @@ private function doProcessNode(Node\Expr $node, Scope $scope, string $keyChecked if (!$constantStringType->isCallable()->yes()) { $errors[] = RuleErrorBuilder::message( sprintf("%s callback %s at key '%s' is not callable.", $keyChecked, $constantStringType->describe(VerbosityLevel::value()), $pos) - )->line($errorLine)->build(); + )->line($errorLine) + ->identifier('renderCallback.nonCallableCallback') + ->build(); } elseif ($this->reflectionProvider->hasFunction(new Name($constantStringType->getValue()), null)) { // We can determine if the callback is callable through the type system. However, we cannot determine // if it is just a function or a static class call (MyClass::staticFunc). $errors[] = RuleErrorBuilder::message( sprintf("%s callback %s at key '%s' is not trusted.", $keyChecked, $constantStringType->describe(VerbosityLevel::value()), $pos) )->line($errorLine) - ->tip('Change record: https://www.drupal.org/node/2966725.') - ->build(); + ->tip('Change record: https://www.drupal.org/node/2966725.') + ->identifier('renderCallback.nonTrustedCallback') + ->build(); } else { // @see \PHPStan\Type\Constant\ConstantStringType::isCallable preg_match('#^([a-zA-Z_\\x7f-\\xff\\\\][a-zA-Z0-9_\\x7f-\\xff\\\\]*)::([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)\\z#', $constantStringType->getValue(), $matches); if (count($matches) === 0) { $errors[] = RuleErrorBuilder::message( sprintf("%s callback %s at key '%s' is not callable.", $keyChecked, $constantStringType->describe(VerbosityLevel::value()), $pos) - )->line($errorLine)->build(); + )->line($errorLine) + ->identifier('renderCallback.nonCallableCallback') + ->build(); } elseif (!$trustedCallbackType->isSuperTypeOf(new ObjectType($matches[1]))->yes()) { $errors[] = RuleErrorBuilder::message( sprintf("%s callback class %s at key '%s' does not implement Drupal\Core\Security\TrustedCallbackInterface.", $keyChecked, $constantStringType->describe(VerbosityLevel::value()), $pos) - )->line($errorLine)->tip('Change record: https://www.drupal.org/node/2966725.')->build(); + )->line($errorLine)->tip('Change record: https://www.drupal.org/node/2966725.') + ->identifier('renderCallback.callbackNotImplementsTrustedCallbackInterface') + ->build(); } } } @@ -194,7 +206,9 @@ private function doProcessNode(Node\Expr $node, Scope $scope, string $keyChecked } $errors[] = RuleErrorBuilder::message( sprintf("%s callback %s at key '%s' is not callable.", $keyChecked, $constantArrayType->describe(VerbosityLevel::value()), $pos) - )->line($errorLine)->build(); + )->line($errorLine) + ->identifier('renderCallback.nonCallableCallback') + ->build(); continue; } $typeAndMethodNames = $constantArrayType->findTypeAndMethodNames(); @@ -226,7 +240,9 @@ function (ClassReflection $reflection) use ($typeAndMethodName) { $constantArrayType->describe(VerbosityLevel::value()), $pos ) - )->line($errorLine)->tip('Change record: https://www.drupal.org/node/3349470')->build(); + )->line($errorLine)->tip('Change record: https://www.drupal.org/node/3349470') + ->identifier('renderCallback.callbackNotImplementsTrustedCallback') + ->build(); } else { $errors[] = RuleErrorBuilder::message( sprintf( @@ -235,7 +251,9 @@ function (ClassReflection $reflection) use ($typeAndMethodName) { $typeAndMethodName->getType()->describe(VerbosityLevel::value()), $pos ) - )->line($errorLine)->tip('Change record: https://www.drupal.org/node/2966725.')->build(); + )->line($errorLine)->tip('Change record: https://www.drupal.org/node/2966725.') + ->identifier('renderCallback.callbackNotImplementsTrustedCallbackInterface') + ->build(); } } } @@ -248,14 +266,18 @@ function (ClassReflection $reflection) use ($typeAndMethodName) { if ($formType->isSuperTypeOf($classType)->yes()) { $errors[] = RuleErrorBuilder::message( sprintf("%s may not contain a closure at key '%s' as forms may be serialized and serialization of closures is not allowed.", $keyChecked, $pos) - )->line($errorLine)->build(); + )->line($errorLine) + ->identifier('renderCallback.containsClosure') + ->build(); } } if (count($errors) === 0 && ($checkIsCallable && !$type->isCallable()->yes())) { $errors[] = RuleErrorBuilder::message( sprintf("%s value '%s' at key '%s' is invalid.", $keyChecked, $type->describe(VerbosityLevel::value()), $pos) - )->line($errorLine)->build(); + )->line($errorLine) + ->identifier('renderCallback.invalidKeyValue') + ->build(); } return $errors; @@ -281,7 +303,7 @@ private function getType(Node\Expr $node, Scope $scope): Type } } } elseif ($type instanceof ConstantStringType) { - if ($type->isClassStringType()->yes()) { + if ($type->isClassString()->yes()) { return $type; } // Covers \Drupal\Core\Controller\ControllerResolver::createController. From abd204e230a17f34361ea88fc2bf753f3222b8d8 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 20:34:14 +0100 Subject: [PATCH 06/22] PHPStan errors, low hanging fruit edition - Part III --- .../DependencySerializationTraitPropertyRule.php | 8 ++++++-- .../EntityQuery/EntityQueryHasAccessCheckRule.php | 4 +++- .../GlobalDrupalDependencyInjectionRule.php | 8 +++++--- src/Rules/Drupal/LoadIncludes.php | 15 +++++++++------ src/Rules/Drupal/ModuleLoadInclude.php | 15 +++++++++------ .../PluginManagerSetsCacheBackendRule.php | 12 ++++++++++-- 6 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/Rules/Drupal/DependencySerializationTraitPropertyRule.php b/src/Rules/Drupal/DependencySerializationTraitPropertyRule.php index 5d35c955..269dd298 100644 --- a/src/Rules/Drupal/DependencySerializationTraitPropertyRule.php +++ b/src/Rules/Drupal/DependencySerializationTraitPropertyRule.php @@ -35,7 +35,9 @@ public function processNode(Node $node, Scope $scope): array '%s does not support private properties.', DependencySerializationTrait::class ) - )->tip('See https://www.drupal.org/node/3110266')->build(); + )->tip('See https://www.drupal.org/node/3110266') + ->identifier('dependencySerializationTraitProperty.unsupportedPrivateProperty') + ->build(); } if ($node->isReadOnly()) { $errors[] = RuleErrorBuilder::message( @@ -43,7 +45,9 @@ public function processNode(Node $node, Scope $scope): array 'Read-only properties are incompatible with %s.', DependencySerializationTrait::class ) - )->tip('See https://www.drupal.org/node/3110266')->build(); + )->tip('See https://www.drupal.org/node/3110266') + ->identifier('dependencySerializationTraitProperty.unsupportedReadOnlyProperty') + ->build(); } return $errors; } diff --git a/src/Rules/Drupal/EntityQuery/EntityQueryHasAccessCheckRule.php b/src/Rules/Drupal/EntityQuery/EntityQueryHasAccessCheckRule.php index 46915b4e..7a914c81 100644 --- a/src/Rules/Drupal/EntityQuery/EntityQueryHasAccessCheckRule.php +++ b/src/Rules/Drupal/EntityQuery/EntityQueryHasAccessCheckRule.php @@ -46,7 +46,9 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message( 'Relying on entity queries to check access by default is deprecated in drupal:9.2.0 and an error will be thrown from drupal:10.0.0. Call \Drupal\Core\Entity\Query\QueryInterface::accessCheck() with TRUE or FALSE to specify whether access should be checked.' - )->tip('See https://www.drupal.org/node/3201242')->build(), + )->tip('See https://www.drupal.org/node/3201242') + ->identifier('entityQueryHasAccessCheck.noAccessCheck') + ->build(), ]; } } diff --git a/src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php b/src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php index 068197cd..8022b753 100644 --- a/src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php +++ b/src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php @@ -6,6 +6,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleErrorBuilder; /** * @implements Rule @@ -71,8 +72,9 @@ public function processNode(Node $node, Scope $scope): array return []; } - return [ - '\Drupal calls should be avoided in classes, use dependency injection instead' - ]; + return + [RuleErrorBuilder::message('\Drupal calls should be avoided in classes, use dependency injection instead') + ->identifier('globalDrupalDependencyInjectio.useDependencyInjection') + ->build()]; } } diff --git a/src/Rules/Drupal/LoadIncludes.php b/src/Rules/Drupal/LoadIncludes.php index 2c2f4a6e..82061e90 100644 --- a/src/Rules/Drupal/LoadIncludes.php +++ b/src/Rules/Drupal/LoadIncludes.php @@ -59,8 +59,9 @@ public function processNode(Node $node, Scope $scope): array ModuleHandlerInterface::class, $moduleName )) - ->line($node->getStartLine()) - ->build() + ->line($node->getStartLine()) + ->identifier('loadIncludes.moduleNotFound') + ->build() ]; } @@ -75,8 +76,9 @@ public function processNode(Node $node, Scope $scope): array $module->getPath() . '/' . $filename, ModuleHandlerInterface::class )) - ->line($node->getStartLine()) - ->build() + ->line($node->getStartLine()) + ->identifier('loadIncludes.fileNotLoadable') + ->build() ]; } catch (Throwable $e) { return [ @@ -84,8 +86,9 @@ public function processNode(Node $node, Scope $scope): array 'A file could not be loaded from %s::loadInclude', ModuleHandlerInterface::class )) - ->line($node->getStartLine()) - ->build() + ->line($node->getStartLine()) + ->identifier('loadIncludes.fileNotLoadable') + ->build() ]; } } diff --git a/src/Rules/Drupal/ModuleLoadInclude.php b/src/Rules/Drupal/ModuleLoadInclude.php index 2170bff1..e88891e6 100644 --- a/src/Rules/Drupal/ModuleLoadInclude.php +++ b/src/Rules/Drupal/ModuleLoadInclude.php @@ -52,8 +52,9 @@ public function processNode(Node $node, Scope $scope): array $filename, $moduleName )) - ->line($node->getStartLine()) - ->build() + ->line($node->getStartLine()) + ->identifier('moduleLoadInclude.moduleNotFound') + ->build() ]; } $file = $module->getAbsolutePath() . DIRECTORY_SEPARATOR . $filename; @@ -66,14 +67,16 @@ public function processNode(Node $node, Scope $scope): array 'File %s could not be loaded from module_load_include.', $module->getPath() . '/' . $filename )) - ->line($node->getStartLine()) - ->build() + ->line($node->getStartLine()) + ->identifier('moduleLoadInclude.moduleNotLoadable') + ->build() ]; } catch (Throwable $e) { return [ RuleErrorBuilder::message('A file could not be loaded from module_load_include') - ->line($node->getStartLine()) - ->build() + ->line($node->getStartLine()) + ->identifier('moduleLoadInclude.moduleNotLoadable') + ->build() ]; } } diff --git a/src/Rules/Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php b/src/Rules/Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php index b053e105..cd9ec098 100644 --- a/src/Rules/Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php +++ b/src/Rules/Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php @@ -5,6 +5,7 @@ use PhpParser\Node; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\Analyser\Scope; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\ShouldNotHappenException; use PHPStan\Type\Type; use function array_map; @@ -88,10 +89,17 @@ public function processNode(Node $node, Scope $scope): array $errors = []; if (!$hasCacheBackendSet) { - $errors[] = 'Missing cache backend declaration for performance.'; + $errors[] = RuleErrorBuilder::message('Missing cache backend declaration for performance.') + ->identifier('pluginManagerSetsCacheBackend.missingCacheBackend') + ->build(); } foreach ($misnamedCacheTagWarnings as $cacheTagWarning) { - $errors[] = sprintf('%s cache tag might be unclear and does not contain the cache key in it.', $cacheTagWarning); + $errors[] = + $errors[] = RuleErrorBuilder::message( + sprintf('%s cache tag might be unclear and does not contain the cache key in it.', $cacheTagWarning) + ) + ->identifier('pluginManagerSetsCacheBackend.unclearCacheTag') + ->build(); } return $errors; From e0c1fffabd92c123802edd2a8e4b1a126b0717cd Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 21:12:44 +0100 Subject: [PATCH 07/22] PHPStan errors, low hanging fruit edition - Part IV --- .../DeprecatedHookImplementation.php | 16 ++++++++++--- .../Deprecations/GetDeprecatedServiceRule.php | 7 +++++- .../StaticServiceDeprecatedServiceRule.php | 7 +++++- ...nyCmfRouteObjectInterfaceConstantsRule.php | 8 +++++-- ...nyCmfRoutingInClassMethodSignatureRule.php | 24 ++++++++++++++----- .../Drupal/Coder/DiscouragedFunctionsRule.php | 10 +++++++- 6 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/Rules/Deprecations/DeprecatedHookImplementation.php b/src/Rules/Deprecations/DeprecatedHookImplementation.php index a40425dc..5f29a081 100644 --- a/src/Rules/Deprecations/DeprecatedHookImplementation.php +++ b/src/Rules/Deprecations/DeprecatedHookImplementation.php @@ -7,6 +7,7 @@ use PhpParser\Node\Stmt\Function_; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use function basename; @@ -56,12 +57,14 @@ public function processNode(Node $node, Scope $scope) : array return $this->buildError( $function_name, $hook_name, + 'useHookFieldWidgetSingleElementFormAlter', 'in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_single_element_form_alter instead.' ); } if (str_starts_with($hook_name, 'hook_field_widget_') && str_ends_with($hook_name, '_form_alter')) { return $this->buildError( $function_name, + 'useHookFieldWidgetWidgetTypeFormAlter', 'hook_field_widget_WIDGET_TYPE_form_alter', 'in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_single_element_WIDGET_TYPE_form_alter instead.' ); @@ -70,6 +73,7 @@ public function processNode(Node $node, Scope $scope) : array return $this->buildError( $function_name, $hook_name, + 'useHookFieldWidgetCompleteFormAlter', 'in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_complete_form_alter instead.' ); } @@ -77,6 +81,7 @@ public function processNode(Node $node, Scope $scope) : array return $this->buildError( $function_name, 'hook_field_widget_multivalue_WIDGET_TYPE_form_alter', + 'useHookFieldWidgetMulitValueWidgetTypeFormAlter', 'in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_complete_WIDGET_TYPE_form_alter instead.' ); } @@ -89,16 +94,21 @@ public function processNode(Node $node, Scope $scope) : array return []; } - return $this->buildError($function_name, $hook_name, $reflection->getDeprecatedDescription()); + return $this->buildError($function_name, $hook_name, 'other', $reflection->getDeprecatedDescription()); } - private function buildError(string $function_name, string $hook_name, ?string $deprecated_description): array + /** + * @return list + */ + private function buildError(string $function_name, string $hook_name, string $identifier, ?string $deprecated_description): array { $deprecated_description = $deprecated_description !== null ? " $deprecated_description" : "."; return [ RuleErrorBuilder::message( "Function $function_name implements $hook_name which is deprecated$deprecated_description", - )->build() + ) + ->identifier("DeprecatedHookImplementation.{}") + ->build() ]; } } diff --git a/src/Rules/Deprecations/GetDeprecatedServiceRule.php b/src/Rules/Deprecations/GetDeprecatedServiceRule.php index 237563c6..6029c86c 100644 --- a/src/Rules/Deprecations/GetDeprecatedServiceRule.php +++ b/src/Rules/Deprecations/GetDeprecatedServiceRule.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleErrorBuilder; /** * @implements Rule @@ -57,7 +58,11 @@ public function processNode(Node $node, Scope $scope): array $service = $this->serviceMap->getService($serviceName->value); if (($service instanceof DrupalServiceDefinition) && $service->isDeprecated()) { - return [$service->getDeprecatedDescription()]; + return [ + RuleErrorBuilder::message($service->getDeprecatedDescription()) + ->identifier('getDeprecatedService.deprecated') + ->build(), + ]; } return []; diff --git a/src/Rules/Deprecations/StaticServiceDeprecatedServiceRule.php b/src/Rules/Deprecations/StaticServiceDeprecatedServiceRule.php index 7637f8bc..023d0d85 100644 --- a/src/Rules/Deprecations/StaticServiceDeprecatedServiceRule.php +++ b/src/Rules/Deprecations/StaticServiceDeprecatedServiceRule.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleErrorBuilder; /** * @implements Rule @@ -66,7 +67,11 @@ public function processNode(Node $node, Scope $scope): array $service = $this->serviceMap->getService($serviceName->value); if (($service instanceof DrupalServiceDefinition) && $service->isDeprecated()) { - return [$service->getDeprecatedDescription()]; + return [ + RuleErrorBuilder::message($service->getDeprecatedDescription()) + ->identifier('staticServiceDeprecatedService.deprecated') + ->build(), + ]; } return []; diff --git a/src/Rules/Deprecations/SymfonyCmfRouteObjectInterfaceConstantsRule.php b/src/Rules/Deprecations/SymfonyCmfRouteObjectInterfaceConstantsRule.php index 7236f836..20a28bb4 100644 --- a/src/Rules/Deprecations/SymfonyCmfRouteObjectInterfaceConstantsRule.php +++ b/src/Rules/Deprecations/SymfonyCmfRouteObjectInterfaceConstantsRule.php @@ -60,14 +60,18 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message( sprintf('The core dependency symfony-cmf/routing is deprecated and %s::%s is not supported.', $className, $constantName) - )->tip('Change record: https://www.drupal.org/node/3151009')->build(), + )->tip('Change record: https://www.drupal.org/node/3151009') + ->identifier('symfonyCmfRouteObjectInterfaceConstants.deprecated') + ->build(), ]; } return [ RuleErrorBuilder::message( sprintf('%s::%s is deprecated and removed in Drupal 10. Use \Drupal\Core\Routing\RouteObjectInterface::%2$s instead.', $className, $constantName) - )->tip('Change record: https://www.drupal.org/node/3151009')->build(), + )->tip('Change record: https://www.drupal.org/node/3151009') + ->identifier('symfonyCmfRouteObjectInterfaceConstants.useRouteObjectInterface') + ->build(), ]; } } diff --git a/src/Rules/Deprecations/SymfonyCmfRoutingInClassMethodSignatureRule.php b/src/Rules/Deprecations/SymfonyCmfRoutingInClassMethodSignatureRule.php index e96adbb6..16981c53 100644 --- a/src/Rules/Deprecations/SymfonyCmfRoutingInClassMethodSignatureRule.php +++ b/src/Rules/Deprecations/SymfonyCmfRoutingInClassMethodSignatureRule.php @@ -66,7 +66,9 @@ public function processNode(Node $node, Scope $scope): array $referencedClass, '\Drupal\Core\Routing\RouteObjectInterface' ) - )->tip('Change record: https://www.drupal.org/node/3151009')->build(); + )->tip('Change record: https://www.drupal.org/node/3151009') + ->identifier('symfonyCmfRoutingInClassMethodSignature.useRouteObjectInterface') + ->build(); } elseif ($cmfRouteProviderInterfaceType->equals($referencedClassType)) { $errors[] = RuleErrorBuilder::message( sprintf( @@ -76,7 +78,9 @@ public function processNode(Node $node, Scope $scope): array $referencedClass, '\Drupal\Core\Routing\RouteProviderInterface' ) - )->tip('Change record: https://www.drupal.org/node/3151009')->build(); + )->tip('Change record: https://www.drupal.org/node/3151009') + ->identifier('symfonyCmfRoutingInClassMethodSignature.useRouteProviderInterface') + ->build(); } elseif ($cmfLazyRouteCollectionType->equals($referencedClassType)) { $errors[] = RuleErrorBuilder::message( sprintf( @@ -86,7 +90,9 @@ public function processNode(Node $node, Scope $scope): array $referencedClass, '\Drupal\Core\Routing\LazyRouteCollection' ) - )->tip('Change record: https://www.drupal.org/node/3151009')->build(); + )->tip('Change record: https://www.drupal.org/node/3151009') + ->identifier('symfonyCmfRoutingInClassMethodSignature.useLazyRouteCollection') + ->build(); } } } @@ -104,7 +110,9 @@ public function processNode(Node $node, Scope $scope): array $returnClass, '\Drupal\Core\Routing\RouteObjectInterface' ) - )->tip('Change record: https://www.drupal.org/node/3151009')->build(); + )->tip('Change record: https://www.drupal.org/node/3151009') + ->identifier('symfonyCmfRoutingInClassMethodSignature.typehintUseRouteObjectInterface') + ->build(); } elseif ($cmfRouteProviderInterfaceType->equals($returnType)) { $errors[] = RuleErrorBuilder::message( sprintf( @@ -114,7 +122,9 @@ public function processNode(Node $node, Scope $scope): array $returnClass, '\Drupal\Core\Routing\RouteProviderInterface' ) - )->tip('Change record: https://www.drupal.org/node/3151009')->build(); + )->tip('Change record: https://www.drupal.org/node/3151009') + ->identifier('symfonyCmfRoutingInClassMethodSignature.typehintUseRouteProviderInterface') + ->build(); } elseif ($cmfLazyRouteCollectionType->equals($returnType)) { $errors[] = RuleErrorBuilder::message( sprintf( @@ -124,7 +134,9 @@ public function processNode(Node $node, Scope $scope): array $returnClass, '\Drupal\Core\Routing\LazyRouteCollection' ) - )->tip('Change record: https://www.drupal.org/node/3151009')->build(); + )->tip('Change record: https://www.drupal.org/node/3151009') + ->identifier('symfonyCmfRoutingInClassMethodSignature.typehintUseLazyRouteCollection') + ->build(); } } return $errors; diff --git a/src/Rules/Drupal/Coder/DiscouragedFunctionsRule.php b/src/Rules/Drupal/Coder/DiscouragedFunctionsRule.php index f6e9c119..307748ce 100644 --- a/src/Rules/Drupal/Coder/DiscouragedFunctionsRule.php +++ b/src/Rules/Drupal/Coder/DiscouragedFunctionsRule.php @@ -6,6 +6,8 @@ use PhpParser\Node\Expr\FuncCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\VerbosityLevel; use function in_array; use function sprintf; use function strtolower; @@ -56,7 +58,13 @@ public function processNode(Node $node, Scope $scope): array ]; if (in_array($name, $discouragedFunctions, true)) { - return [sprintf('Calls to function %s should not exist.', $name)]; + return [ + RuleErrorBuilder::message( + sprintf('Calls to function %s should not exist.', $name) + ) + ->identifier('discouragedFunctions.discouragedFunctionCalled') + ->build() + ]; } return []; } From dfb2748238b4a116ec7968d133dcd319517ef0b2 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 21:45:45 +0100 Subject: [PATCH 08/22] PHPStan errors, low hanging fruit edition - Part V --- ...ntityFieldsViaMagicReflectionExtension.php | 4 +-- .../FieldItemListMethodReflection.php | 2 +- .../Classes/ClassExtendsInternalClassRule.php | 35 ++++++++++++++----- .../Classes/PluginManagerInspectionRule.php | 18 ++++++++-- .../Deprecations/AccessDeprecatedConstant.php | 7 +++- ...CreateInstanceContextConfigurationRule.php | 1 + .../ConfigEntityConfigExportRule.php | 9 ++++- .../DeprecatedAnnotationsRuleBase.php | 4 +++ .../DeprecatedHookImplementation.php | 2 +- ...PluginAnnotationContextDefinitionsRule.php | 11 +++++- 10 files changed, 74 insertions(+), 19 deletions(-) diff --git a/src/Reflection/EntityFieldsViaMagicReflectionExtension.php b/src/Reflection/EntityFieldsViaMagicReflectionExtension.php index 5261e926..c78efce4 100644 --- a/src/Reflection/EntityFieldsViaMagicReflectionExtension.php +++ b/src/Reflection/EntityFieldsViaMagicReflectionExtension.php @@ -6,7 +6,7 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\PropertiesClassReflectionExtension; use PHPStan\Reflection\PropertyReflection; -use PHPStan\TrinaryLogic; +use PHPStan\Type\IsSuperTypeOfResult; use PHPStan\Type\ObjectType; use function array_key_exists; @@ -63,7 +63,7 @@ public function getProperty(ClassReflection $classReflection, string $propertyNa throw new LogicException($classReflection->getName() . "::$propertyName should be handled earlier."); } - public static function classObjectIsSuperOfInterface(string $name, ObjectType $interfaceObject) : TrinaryLogic + public static function classObjectIsSuperOfInterface(string $name, ObjectType $interfaceObject) : IsSuperTypeOfResult { return $interfaceObject->isSuperTypeOf(new ObjectType($name)); } diff --git a/src/Reflection/FieldItemListMethodReflection.php b/src/Reflection/FieldItemListMethodReflection.php index 7204279f..69802552 100644 --- a/src/Reflection/FieldItemListMethodReflection.php +++ b/src/Reflection/FieldItemListMethodReflection.php @@ -61,7 +61,7 @@ public function getPrototype(): ClassMemberReflection } /** - * @return \PHPStan\Reflection\ParametersAcceptor[] + * @return list<\PHPStan\Reflection\ParametersAcceptor> */ public function getVariants(): array { diff --git a/src/Rules/Classes/ClassExtendsInternalClassRule.php b/src/Rules/Classes/ClassExtendsInternalClassRule.php index 2384b6f0..3467df39 100644 --- a/src/Rules/Classes/ClassExtendsInternalClassRule.php +++ b/src/Rules/Classes/ClassExtendsInternalClassRule.php @@ -7,6 +7,7 @@ use PhpParser\Node\Stmt\Class_; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use function sprintf; @@ -48,34 +49,50 @@ public function processNode(Node $node, Scope $scope): array } if (!isset($node->namespacedName)) { - return [$this->buildError(null, $extendedClassName)->build()]; + return $this->buildError(null, $extendedClassName, null); } $currentClassName = $node->namespacedName->toString(); if (!NamespaceCheck::isDrupalNamespace($node)) { - return [$this->buildError($currentClassName, $extendedClassName)->build()]; + return $this->buildError($currentClassName, $extendedClassName, null); } if (NamespaceCheck::isSharedNamespace($node)) { return []; } - $errorBuilder = $this->buildError($currentClassName, $extendedClassName); + $errorBuilder = []; if ($extendedClassName === 'Drupal\Core\Entity\ContentEntityDeleteForm') { - $errorBuilder->tip('Extend \Drupal\Core\Entity\ContentEntityConfirmFormBase. See https://www.drupal.org/node/2491057'); + $errorBuilder = $this->buildError( + $currentClassName, + $extendedClassName, + 'Extend \Drupal\Core\Entity\ContentEntityConfirmFormBase. See https://www.drupal.org/node/2491057' + ); } elseif ((string) $node->extends->slice(0, 2) === 'Drupal\Core') { - $errorBuilder->tip('Read the Drupal core backwards compatibility and internal API policy: https://www.drupal.org/about/core/policies/core-change-policies/drupal-8-and-9-backwards-compatibility-and-internal-api#internal'); + $errorBuilder = $this->buildError( + $currentClassName, + $extendedClassName, + 'Read the Drupal core backwards compatibility and internal API policy: https://www.drupal.org/about/core/policies/core-change-policies/drupal-8-and-9-backwards-compatibility-and-internal-api#internal' + ); } - return [$errorBuilder->build()]; + return $errorBuilder; } - private function buildError(?string $currentClassName, string $extendedClassName): RuleErrorBuilder + /** + * @return list + */ + private function buildError(?string $currentClassName, string $extendedClassName, ?string $tip): array { - return RuleErrorBuilder::message(sprintf( + $ruleErrorBuilder = RuleErrorBuilder::message(sprintf( '%s extends @internal class %s.', $currentClassName !== null ? sprintf('Class %s', $currentClassName) : 'Anonymous class', $extendedClassName - )); + ))->identifier('classExtendsInternalClass.classExtendsInternalClass'); + if ($tip !== null) { + $ruleErrorBuilder->tip($tip); + } + + return [$ruleErrorBuilder->build()]; } } diff --git a/src/Rules/Classes/PluginManagerInspectionRule.php b/src/Rules/Classes/PluginManagerInspectionRule.php index 9f3a7b12..6437fc2a 100644 --- a/src/Rules/Classes/PluginManagerInspectionRule.php +++ b/src/Rules/Classes/PluginManagerInspectionRule.php @@ -8,6 +8,7 @@ use PhpParser\NodeFinder; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ObjectType; @@ -78,9 +79,9 @@ public function processNode(Node $node, Scope $scope): array $errors[] = RuleErrorBuilder::message( 'Plugin managers should call alterInfo to allow plugin definitions to be altered.' ) - ->identifier('plugin.manager.alterInfoMissing') ->tip('For example, to invoke hook_mymodule_data_alter() call alterInfo with "mymodule_data".') ->line($node->getStartLine()) + ->identifier('pluginManagerInspection.callAlterInfo') ->build(); } @@ -110,6 +111,9 @@ private function isYamlDiscovery(Node\Stmt\Class_ $class): bool return false; } + /** + * @return list + */ private function inspectYamlPluginManager(Node\Stmt\Class_ $class, Node\Stmt\ClassMethod $constructorMethodNode): array { $errors = []; @@ -119,7 +123,11 @@ private function inspectYamlPluginManager(Node\Stmt\Class_ $class, Node\Stmt\Cla $constructor = $reflection->getConstructor(); if ($constructor->getDeclaringClass()->getName() !== $fqn) { - $errors[] = sprintf('%s must override __construct if using YAML plugins.', $fqn); + $errors[] = RuleErrorBuilder::message( + 'Plugin managers should call alterInfo to allow plugin definitions to be altered.' + ) + ->identifier('pluginManagerInspection.callAlterInfo') + ->build(); } else { foreach ($constructorMethodNode->stmts ?? [] as $constructorStmt) { if ($constructorStmt instanceof Node\Stmt\Expression) { @@ -130,7 +138,11 @@ private function inspectYamlPluginManager(Node\Stmt\Class_ $class, Node\Stmt\Cla && ((string)$constructorStmt->class === 'parent') && $constructorStmt->name instanceof Node\Identifier && $constructorStmt->name->name === '__construct') { - $errors[] = 'YAML plugin managers should not invoke its parent constructor.'; + $errors[] = RuleErrorBuilder::message( + 'YAML plugin managers should not invoke its parent constructor.' + ) + ->identifier('pluginManagerInspection.yamlPluginManagersInvokesParentConstructor') + ->build(); } } } diff --git a/src/Rules/Deprecations/AccessDeprecatedConstant.php b/src/Rules/Deprecations/AccessDeprecatedConstant.php index e7e0fd83..e47661b3 100644 --- a/src/Rules/Deprecations/AccessDeprecatedConstant.php +++ b/src/Rules/Deprecations/AccessDeprecatedConstant.php @@ -8,6 +8,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleErrorBuilder; use function array_merge; use function explode; use function sprintf; @@ -124,7 +125,11 @@ public function processNode(Node $node, Scope $scope): array $constantName = $this->reflectionProvider->resolveConstantName($node->name, $scope); if (isset($deprecatedConstants[$constantName])) { return [ - sprintf('Call to deprecated constant %s: %s', $constantName, $deprecatedConstants[$constantName]) + RuleErrorBuilder::message( + sprintf('Call to deprecated constant %s: %s', $constantName, $deprecatedConstants[$constantName]) + ) + ->identifier("accessDeprecatedConstant.deprecatedConstantCalled") + ->build() ]; } return []; diff --git a/src/Rules/Deprecations/ConditionManagerCreateInstanceContextConfigurationRule.php b/src/Rules/Deprecations/ConditionManagerCreateInstanceContextConfigurationRule.php index d8a26cb9..0d037092 100644 --- a/src/Rules/Deprecations/ConditionManagerCreateInstanceContextConfigurationRule.php +++ b/src/Rules/Deprecations/ConditionManagerCreateInstanceContextConfigurationRule.php @@ -51,6 +51,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message('Passing context values to plugins via configuration is deprecated in drupal:9.1.0 and will be removed before drupal:10.0.0. Instead, call ::setContextValue() on the plugin itself. See https://www.drupal.org/node/3120980') ->line($node->getStartLine()) + ->identifier("conditionManagerCreateInstanceContextConfiguration.callSetContextValue") ->build() ]; } diff --git a/src/Rules/Deprecations/ConfigEntityConfigExportRule.php b/src/Rules/Deprecations/ConfigEntityConfigExportRule.php index 1c4bcf5a..77c067b3 100644 --- a/src/Rules/Deprecations/ConfigEntityConfigExportRule.php +++ b/src/Rules/Deprecations/ConfigEntityConfigExportRule.php @@ -7,6 +7,8 @@ use PHPStan\PhpDoc\ResolvedPhpDocBlock; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\Reflection\ClassReflection; +use PHPStan\Rules\IdentifierRuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\ShouldNotHappenException; use function preg_match; @@ -18,6 +20,9 @@ protected function getExpectedInterface(): string return 'Drupal\Core\Config\Entity\ConfigEntityInterface'; } + /** + * @return list + */ protected function doProcessNode(ClassReflection $reflection, Node\Stmt\Class_ $node, Scope $scope): array { $phpDoc = $reflection->getResolvedPhpDoc(); @@ -32,7 +37,9 @@ protected function doProcessNode(ClassReflection $reflection, Node\Stmt\Class_ $ } if ($hasMatch === 0) { return [ - 'Configuration entity must define a `config_export` key. See https://www.drupal.org/node/2481909', + RuleErrorBuilder::message('Configuration entity must define a `config_export` key. See https://www.drupal.org/node/2481909') + ->identifier('configEntityConfigExport.noConfigExportKey') + ->build() ]; } return []; diff --git a/src/Rules/Deprecations/DeprecatedAnnotationsRuleBase.php b/src/Rules/Deprecations/DeprecatedAnnotationsRuleBase.php index 8f94f969..0c1b5210 100644 --- a/src/Rules/Deprecations/DeprecatedAnnotationsRuleBase.php +++ b/src/Rules/Deprecations/DeprecatedAnnotationsRuleBase.php @@ -6,6 +6,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; /** @@ -31,6 +32,9 @@ public function getNodeType(): string abstract protected function getExpectedInterface(): string; + /** + * @return list + */ abstract protected function doProcessNode( ClassReflection $reflection, Node\Stmt\Class_ $node, diff --git a/src/Rules/Deprecations/DeprecatedHookImplementation.php b/src/Rules/Deprecations/DeprecatedHookImplementation.php index 5f29a081..b9c66687 100644 --- a/src/Rules/Deprecations/DeprecatedHookImplementation.php +++ b/src/Rules/Deprecations/DeprecatedHookImplementation.php @@ -107,7 +107,7 @@ private function buildError(string $function_name, string $hook_name, string $id RuleErrorBuilder::message( "Function $function_name implements $hook_name which is deprecated$deprecated_description", ) - ->identifier("DeprecatedHookImplementation.{}") + ->identifier("deprecatedHookImplementation.{}") ->build() ]; } diff --git a/src/Rules/Deprecations/PluginAnnotationContextDefinitionsRule.php b/src/Rules/Deprecations/PluginAnnotationContextDefinitionsRule.php index 814ae8e8..ae618cc9 100644 --- a/src/Rules/Deprecations/PluginAnnotationContextDefinitionsRule.php +++ b/src/Rules/Deprecations/PluginAnnotationContextDefinitionsRule.php @@ -5,6 +5,8 @@ use PhpParser\Node; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; +use PHPStan\Rules\IdentifierRuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\ShouldNotHappenException; use function preg_match; @@ -16,6 +18,9 @@ protected function getExpectedInterface(): string return 'Drupal\Component\Plugin\ContextAwarePluginInterface'; } + /** + * @return list + */ protected function doProcessNode(ClassReflection $reflection, Node\Stmt\Class_ $node, Scope $scope): array { $annotation = $reflection->getResolvedPhpDoc(); @@ -30,7 +35,11 @@ protected function doProcessNode(ClassReflection $reflection, Node\Stmt\Class_ $ } if ($hasMatch === 1) { return [ - 'Providing context definitions via the "context" key is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use the "context_definitions" key instead.', + RuleErrorBuilder::message( + 'Providing context definitions via the "context" key is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use the "context_definitions" key instead.', + ) + ->identifier('pluginAnnotationContextDefinitions.useContextDefinitionsKey') + ->build() ]; } return []; From 5b9b1c220fa842273db94038fdca982f1c21c16f Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 21:56:08 +0100 Subject: [PATCH 09/22] PHPUnit fix --- src/Rules/Deprecations/DeprecatedHookImplementation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rules/Deprecations/DeprecatedHookImplementation.php b/src/Rules/Deprecations/DeprecatedHookImplementation.php index b9c66687..03897545 100644 --- a/src/Rules/Deprecations/DeprecatedHookImplementation.php +++ b/src/Rules/Deprecations/DeprecatedHookImplementation.php @@ -107,7 +107,7 @@ private function buildError(string $function_name, string $hook_name, string $id RuleErrorBuilder::message( "Function $function_name implements $hook_name which is deprecated$deprecated_description", ) - ->identifier("deprecatedHookImplementation.{}") + ->identifier("deprecatedHookImplementation.{$identifier}") ->build() ]; } From e2f39c56641dfd0d10231217fb77aebfb66bf286 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 22:03:41 +0100 Subject: [PATCH 10/22] PHPCS fixes --- src/Rules/Classes/ClassExtendsInternalClassRule.php | 3 +-- src/Rules/Classes/PluginManagerInspectionRule.php | 4 +--- src/Rules/Deprecations/ConfigEntityConfigExportRule.php | 3 +-- src/Rules/Deprecations/DeprecatedAnnotationsRuleBase.php | 3 +-- src/Rules/Deprecations/DeprecatedHookImplementation.php | 3 +-- .../Deprecations/PluginAnnotationContextDefinitionsRule.php | 3 +-- src/Rules/Drupal/Coder/DiscouragedFunctionsRule.php | 1 - 7 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/Rules/Classes/ClassExtendsInternalClassRule.php b/src/Rules/Classes/ClassExtendsInternalClassRule.php index 3467df39..d4edd2ad 100644 --- a/src/Rules/Classes/ClassExtendsInternalClassRule.php +++ b/src/Rules/Classes/ClassExtendsInternalClassRule.php @@ -7,7 +7,6 @@ use PhpParser\Node\Stmt\Class_; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ReflectionProvider; -use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use function sprintf; @@ -80,7 +79,7 @@ public function processNode(Node $node, Scope $scope): array } /** - * @return list + * @return list<\PHPStan\Rules\IdentifierRuleError> */ private function buildError(?string $currentClassName, string $extendedClassName, ?string $tip): array { diff --git a/src/Rules/Classes/PluginManagerInspectionRule.php b/src/Rules/Classes/PluginManagerInspectionRule.php index 6437fc2a..6162352e 100644 --- a/src/Rules/Classes/PluginManagerInspectionRule.php +++ b/src/Rules/Classes/PluginManagerInspectionRule.php @@ -8,11 +8,9 @@ use PhpParser\NodeFinder; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ReflectionProvider; -use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ObjectType; -use function sprintf; /** * @implements \PHPStan\Rules\Rule<\PhpParser\Node\Stmt\Class_> @@ -112,7 +110,7 @@ private function isYamlDiscovery(Node\Stmt\Class_ $class): bool } /** - * @return list + * @return list<\PHPStan\Rules\IdentifierRuleError> */ private function inspectYamlPluginManager(Node\Stmt\Class_ $class, Node\Stmt\ClassMethod $constructorMethodNode): array { diff --git a/src/Rules/Deprecations/ConfigEntityConfigExportRule.php b/src/Rules/Deprecations/ConfigEntityConfigExportRule.php index 77c067b3..ff029d7d 100644 --- a/src/Rules/Deprecations/ConfigEntityConfigExportRule.php +++ b/src/Rules/Deprecations/ConfigEntityConfigExportRule.php @@ -7,7 +7,6 @@ use PHPStan\PhpDoc\ResolvedPhpDocBlock; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\Reflection\ClassReflection; -use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\ShouldNotHappenException; use function preg_match; @@ -21,7 +20,7 @@ protected function getExpectedInterface(): string } /** - * @return list + * @return list<\PHPStan\Rules\IdentifierRuleError> */ protected function doProcessNode(ClassReflection $reflection, Node\Stmt\Class_ $node, Scope $scope): array { diff --git a/src/Rules/Deprecations/DeprecatedAnnotationsRuleBase.php b/src/Rules/Deprecations/DeprecatedAnnotationsRuleBase.php index 0c1b5210..50bb673e 100644 --- a/src/Rules/Deprecations/DeprecatedAnnotationsRuleBase.php +++ b/src/Rules/Deprecations/DeprecatedAnnotationsRuleBase.php @@ -6,7 +6,6 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ReflectionProvider; -use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; /** @@ -33,7 +32,7 @@ public function getNodeType(): string abstract protected function getExpectedInterface(): string; /** - * @return list + * @return list<\PHPStan\Rules\IdentifierRuleError> */ abstract protected function doProcessNode( ClassReflection $reflection, diff --git a/src/Rules/Deprecations/DeprecatedHookImplementation.php b/src/Rules/Deprecations/DeprecatedHookImplementation.php index 03897545..e73c0ed6 100644 --- a/src/Rules/Deprecations/DeprecatedHookImplementation.php +++ b/src/Rules/Deprecations/DeprecatedHookImplementation.php @@ -7,7 +7,6 @@ use PhpParser\Node\Stmt\Function_; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ReflectionProvider; -use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use function basename; @@ -98,7 +97,7 @@ public function processNode(Node $node, Scope $scope) : array } /** - * @return list + * @return list<\PHPStan\Rules\IdentifierRuleError> */ private function buildError(string $function_name, string $hook_name, string $identifier, ?string $deprecated_description): array { diff --git a/src/Rules/Deprecations/PluginAnnotationContextDefinitionsRule.php b/src/Rules/Deprecations/PluginAnnotationContextDefinitionsRule.php index ae618cc9..83b1701c 100644 --- a/src/Rules/Deprecations/PluginAnnotationContextDefinitionsRule.php +++ b/src/Rules/Deprecations/PluginAnnotationContextDefinitionsRule.php @@ -5,7 +5,6 @@ use PhpParser\Node; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\ShouldNotHappenException; use function preg_match; @@ -19,7 +18,7 @@ protected function getExpectedInterface(): string } /** - * @return list + * @return list<\PHPStan\Rules\IdentifierRuleError> */ protected function doProcessNode(ClassReflection $reflection, Node\Stmt\Class_ $node, Scope $scope): array { diff --git a/src/Rules/Drupal/Coder/DiscouragedFunctionsRule.php b/src/Rules/Drupal/Coder/DiscouragedFunctionsRule.php index 307748ce..42033775 100644 --- a/src/Rules/Drupal/Coder/DiscouragedFunctionsRule.php +++ b/src/Rules/Drupal/Coder/DiscouragedFunctionsRule.php @@ -7,7 +7,6 @@ use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; -use PHPStan\Type\VerbosityLevel; use function in_array; use function sprintf; use function strtolower; From ab7797d2802d440a76b5d91e3a9b8b3f7049d838 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Mon, 11 Nov 2024 22:08:24 +0100 Subject: [PATCH 11/22] PHPUnit fix --- .../Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Rules/Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php b/src/Rules/Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php index cd9ec098..dcd042f9 100644 --- a/src/Rules/Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php +++ b/src/Rules/Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php @@ -94,7 +94,6 @@ public function processNode(Node $node, Scope $scope): array ->build(); } foreach ($misnamedCacheTagWarnings as $cacheTagWarning) { - $errors[] = $errors[] = RuleErrorBuilder::message( sprintf('%s cache tag might be unclear and does not contain the cache key in it.', $cacheTagWarning) ) From d1b8eecd2ad40180d53579818ad4dbcaf66ca3a2 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 09:38:04 +0100 Subject: [PATCH 12/22] More brain = less errors --- .../Classes/ClassExtendsInternalClassRule.php | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Rules/Classes/ClassExtendsInternalClassRule.php b/src/Rules/Classes/ClassExtendsInternalClassRule.php index d4edd2ad..dff817f1 100644 --- a/src/Rules/Classes/ClassExtendsInternalClassRule.php +++ b/src/Rules/Classes/ClassExtendsInternalClassRule.php @@ -61,21 +61,18 @@ public function processNode(Node $node, Scope $scope): array return []; } - $errorBuilder = []; + $tip = null; if ($extendedClassName === 'Drupal\Core\Entity\ContentEntityDeleteForm') { - $errorBuilder = $this->buildError( - $currentClassName, - $extendedClassName, - 'Extend \Drupal\Core\Entity\ContentEntityConfirmFormBase. See https://www.drupal.org/node/2491057' - ); + $tip = 'Extend \Drupal\Core\Entity\ContentEntityConfirmFormBase. See https://www.drupal.org/node/2491057'; } elseif ((string) $node->extends->slice(0, 2) === 'Drupal\Core') { - $errorBuilder = $this->buildError( - $currentClassName, - $extendedClassName, - 'Read the Drupal core backwards compatibility and internal API policy: https://www.drupal.org/about/core/policies/core-change-policies/drupal-8-and-9-backwards-compatibility-and-internal-api#internal' - ); + $tip = 'Read the Drupal core backwards compatibility and internal API policy: https://www.drupal.org/about/core/policies/core-change-policies/drupal-8-and-9-backwards-compatibility-and-internal-api#internal'; } - return $errorBuilder; + + return $this->buildError( + $currentClassName, + $extendedClassName, + $tip + ); } /** From 80b6a4abdd0fb66c005cea0252cee859a93f8bd2 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 09:45:39 +0100 Subject: [PATCH 13/22] PHPUnit D9 fix --- src/Rules/Deprecations/DeprecatedHookImplementation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rules/Deprecations/DeprecatedHookImplementation.php b/src/Rules/Deprecations/DeprecatedHookImplementation.php index e73c0ed6..561eb66c 100644 --- a/src/Rules/Deprecations/DeprecatedHookImplementation.php +++ b/src/Rules/Deprecations/DeprecatedHookImplementation.php @@ -63,8 +63,8 @@ public function processNode(Node $node, Scope $scope) : array if (str_starts_with($hook_name, 'hook_field_widget_') && str_ends_with($hook_name, '_form_alter')) { return $this->buildError( $function_name, - 'useHookFieldWidgetWidgetTypeFormAlter', 'hook_field_widget_WIDGET_TYPE_form_alter', + 'useHookFieldWidgetWidgetTypeFormAlter', 'in drupal:9.2.0 and is removed from drupal:10.0.0. Use hook_field_widget_single_element_WIDGET_TYPE_form_alter instead.' ); } From dca38ddb795d93412b30128ead22617a02badf58 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 09:55:42 +0100 Subject: [PATCH 14/22] Temporary here until drupal core is also on phpstan/*:^2. --- .github/workflows/php.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index da0f1695..da96c0db 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -121,6 +121,10 @@ jobs: - name: "require phpstan-drupal" run: | cd ~/drupal + echo "Temporary here until drupal core is also on phpstan/*:^2.0" + composer require "phpstan/extension-installer:^2.0" + composer require "phpstan/phpstan:^2.0" + composer require "phpstan/phpstan-phpunit:^2.0" COMPOSER_MEMORY_LIMIT=-1 composer require mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 1.2.99" phpstan/extension-installer --with-all-dependencies cp $GITHUB_WORKSPACE/tests/fixtures/config/drupal-phpstan.neon phpstan.neon - name: "Test core/install.php" From ca5d4a24c3b78046c930f3eecd7eebcb33febf72 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 10:01:50 +0100 Subject: [PATCH 15/22] Temporary here until drupal core is also on phpstan/*:^2. --- .github/workflows/php.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index da96c0db..880bdaa9 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -122,7 +122,6 @@ jobs: run: | cd ~/drupal echo "Temporary here until drupal core is also on phpstan/*:^2.0" - composer require "phpstan/extension-installer:^2.0" composer require "phpstan/phpstan:^2.0" composer require "phpstan/phpstan-phpunit:^2.0" COMPOSER_MEMORY_LIMIT=-1 composer require mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 1.2.99" phpstan/extension-installer --with-all-dependencies From 3e5b5cc8e0194c0b72d2541ce2a5ca63b22fadaa Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 10:07:10 +0100 Subject: [PATCH 16/22] Temporary here until drupal core is also on phpstan/*:^2. --- .github/workflows/php.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 880bdaa9..d4728e4d 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -122,8 +122,8 @@ jobs: run: | cd ~/drupal echo "Temporary here until drupal core is also on phpstan/*:^2.0" - composer require "phpstan/phpstan:^2.0" - composer require "phpstan/phpstan-phpunit:^2.0" + composer require "phpstan/phpstan:^2.0" -W + composer require "phpstan/phpstan-phpunit:^2.0" -W COMPOSER_MEMORY_LIMIT=-1 composer require mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 1.2.99" phpstan/extension-installer --with-all-dependencies cp $GITHUB_WORKSPACE/tests/fixtures/config/drupal-phpstan.neon phpstan.neon - name: "Test core/install.php" From e7d6f2f67337a911ebabfd94eee75adeec8697a5 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 10:17:40 +0100 Subject: [PATCH 17/22] Yawn... --- .github/workflows/php.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index d4728e4d..f420d995 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -121,10 +121,7 @@ jobs: - name: "require phpstan-drupal" run: | cd ~/drupal - echo "Temporary here until drupal core is also on phpstan/*:^2.0" - composer require "phpstan/phpstan:^2.0" -W - composer require "phpstan/phpstan-phpunit:^2.0" -W - COMPOSER_MEMORY_LIMIT=-1 composer require mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 1.2.99" phpstan/extension-installer --with-all-dependencies + COMPOSER_MEMORY_LIMIT=-1 composer require --dev "phpstan/phpstan:^2.0" "phpstan/phpstan-phpunit:^2.0" mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 1.2.99" phpstan/extension-installer --with-all-dependencies cp $GITHUB_WORKSPACE/tests/fixtures/config/drupal-phpstan.neon phpstan.neon - name: "Test core/install.php" run: | From 60f74484f3b6f9ab339d8a28fc52d2de9fdc482a Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 10:28:59 +0100 Subject: [PATCH 18/22] Yawn... --- .github/workflows/php.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index f420d995..73869dc9 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -121,7 +121,7 @@ jobs: - name: "require phpstan-drupal" run: | cd ~/drupal - COMPOSER_MEMORY_LIMIT=-1 composer require --dev "phpstan/phpstan:^2.0" "phpstan/phpstan-phpunit:^2.0" mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 1.2.99" phpstan/extension-installer --with-all-dependencies + COMPOSER_MEMORY_LIMIT=-1 composer require drupal/core-dev "phpstan/phpstan:^2.0" "phpstan/phpstan-phpunit:^2.0" mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 1.2.99" phpstan/extension-installer --with-all-dependencies cp $GITHUB_WORKSPACE/tests/fixtures/config/drupal-phpstan.neon phpstan.neon - name: "Test core/install.php" run: | From 28bee948837743f1789ff39d19e64e85af74b4b2 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 10:35:35 +0100 Subject: [PATCH 19/22] FFS WTFBBQ --- .github/workflows/php.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 73869dc9..ccf15bcb 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -121,7 +121,7 @@ jobs: - name: "require phpstan-drupal" run: | cd ~/drupal - COMPOSER_MEMORY_LIMIT=-1 composer require drupal/core-dev "phpstan/phpstan:^2.0" "phpstan/phpstan-phpunit:^2.0" mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 1.2.99" phpstan/extension-installer --with-all-dependencies + COMPOSER_MEMORY_LIMIT=-1 composer require drupal/core-dev "phpstan/phpstan:^2.0" "phpstan/phpstan-phpunit:^2.0" mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 2.99.99" phpstan/extension-installer --with-all-dependencies cp $GITHUB_WORKSPACE/tests/fixtures/config/drupal-phpstan.neon phpstan.neon - name: "Test core/install.php" run: | @@ -253,7 +253,7 @@ jobs: - name: "require phpstan-drupal" run: | cd ${{ runner.temp }}/drupal - composer require --dev mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 1.1.99" --with-all-dependencies + composer require drupal/core-dev "phpstan/phpstan:^2.0" "phpstan/phpstan-phpunit:^2.0" mglaman/phpstan-drupal "${{ steps.branch_alias.outputs.VERSION_ALIAS }} as 2.99.99" phpstan/extension-installer --with-all-dependencies - name: "Check baseline" run: | cd ${{ runner.temp }}/drupal From eb91a7a1e4b63b8ce1e5870d0b508d706522b2b3 Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 11:24:00 +0100 Subject: [PATCH 20/22] Cleanup --- src/Rules/Classes/PluginManagerInspectionRule.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Rules/Classes/PluginManagerInspectionRule.php b/src/Rules/Classes/PluginManagerInspectionRule.php index 6162352e..e580fc84 100644 --- a/src/Rules/Classes/PluginManagerInspectionRule.php +++ b/src/Rules/Classes/PluginManagerInspectionRule.php @@ -11,6 +11,7 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ObjectType; +use function sprintf; /** * @implements \PHPStan\Rules\Rule<\PhpParser\Node\Stmt\Class_> @@ -79,7 +80,7 @@ public function processNode(Node $node, Scope $scope): array ) ->tip('For example, to invoke hook_mymodule_data_alter() call alterInfo with "mymodule_data".') ->line($node->getStartLine()) - ->identifier('pluginManagerInspection.callAlterInfo') + ->identifier('pluginManagerInspection.alterInfoMissing') ->build(); } @@ -122,7 +123,7 @@ private function inspectYamlPluginManager(Node\Stmt\Class_ $class, Node\Stmt\Cla if ($constructor->getDeclaringClass()->getName() !== $fqn) { $errors[] = RuleErrorBuilder::message( - 'Plugin managers should call alterInfo to allow plugin definitions to be altered.' + $errors[] = sprintf('%s must override __construct if using YAML plugins.', $fqn) ) ->identifier('pluginManagerInspection.callAlterInfo') ->build(); From cb926103ec6f8a87320239c77d681ce0a13dc88c Mon Sep 17 00:00:00 2001 From: Frank Ebbers Date: Tue, 12 Nov 2024 11:27:20 +0100 Subject: [PATCH 21/22] More brain = less errors --- src/Rules/Classes/PluginManagerInspectionRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rules/Classes/PluginManagerInspectionRule.php b/src/Rules/Classes/PluginManagerInspectionRule.php index e580fc84..8ad311ce 100644 --- a/src/Rules/Classes/PluginManagerInspectionRule.php +++ b/src/Rules/Classes/PluginManagerInspectionRule.php @@ -123,7 +123,7 @@ private function inspectYamlPluginManager(Node\Stmt\Class_ $class, Node\Stmt\Cla if ($constructor->getDeclaringClass()->getName() !== $fqn) { $errors[] = RuleErrorBuilder::message( - $errors[] = sprintf('%s must override __construct if using YAML plugins.', $fqn) + sprintf('%s must override __construct if using YAML plugins.', $fqn) ) ->identifier('pluginManagerInspection.callAlterInfo') ->build(); From fc921098d84a666214464b109a244965c2fb2faf Mon Sep 17 00:00:00 2001 From: Boegie <18569707+Boegie@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:11:53 +0100 Subject: [PATCH 22/22] Update src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php Co-authored-by: Matt Glaman --- src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php b/src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php index 8022b753..b073f468 100644 --- a/src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php +++ b/src/Rules/Drupal/GlobalDrupalDependencyInjectionRule.php @@ -74,7 +74,7 @@ public function processNode(Node $node, Scope $scope): array return [RuleErrorBuilder::message('\Drupal calls should be avoided in classes, use dependency injection instead') - ->identifier('globalDrupalDependencyInjectio.useDependencyInjection') + ->identifier('globalDrupalDependencyInjection.useDependencyInjection') ->build()]; } }