From 2b207f9fb375abb520d1d99ae60220ea7cb6c65d Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Thu, 18 Jul 2024 15:54:38 +1200 Subject: [PATCH] NEW Add rule to avoid use of "new" keyword --- src/PHPStan/KeywordSelfRule.php | 73 +++++++++++++++++++ tests/PHPStan/KeywordSelfRuleTest.php | 62 ++++++++++++++++ .../KeywordSelfRuleTest/Anothertest.php | 0 .../KeywordSelfRuleTest/ClassUsesTrait.php | 11 +++ .../ClassUsesTraitCorrect.php | 11 +++ .../PHPStan/KeywordSelfRuleTest/TestClass.php | 29 ++++++++ .../KeywordSelfRuleTest/TestInterface.php | 21 ++++++ .../PHPStan/KeywordSelfRuleTest/TestTrait.php | 28 +++++++ .../KeywordSelfRuleTest/TestTraitCorrect.php | 28 +++++++ 9 files changed, 263 insertions(+) create mode 100644 src/PHPStan/KeywordSelfRule.php create mode 100644 tests/PHPStan/KeywordSelfRuleTest.php create mode 100644 tests/PHPStan/KeywordSelfRuleTest/Anothertest.php create mode 100644 tests/PHPStan/KeywordSelfRuleTest/ClassUsesTrait.php create mode 100644 tests/PHPStan/KeywordSelfRuleTest/ClassUsesTraitCorrect.php create mode 100644 tests/PHPStan/KeywordSelfRuleTest/TestClass.php create mode 100644 tests/PHPStan/KeywordSelfRuleTest/TestInterface.php create mode 100644 tests/PHPStan/KeywordSelfRuleTest/TestTrait.php create mode 100644 tests/PHPStan/KeywordSelfRuleTest/TestTraitCorrect.php diff --git a/src/PHPStan/KeywordSelfRule.php b/src/PHPStan/KeywordSelfRule.php new file mode 100644 index 0000000..802a847 --- /dev/null +++ b/src/PHPStan/KeywordSelfRule.php @@ -0,0 +1,73 @@ + + */ +class KeywordSelfRule implements Rule +{ + public function getNodeType(): string + { + return Node::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $gettingConst = false; + switch (get_class($node)) { + // fetching a constant, e.g. `self::MY_CONST` + case ClassConstFetch::class: + // Traits can use `self` to get const values - but otherwise follow same + // logic as methods or properties. + if ($scope->isInTrait()) { + return []; + } + // static method call, e.g. `self::myMethod()` + case StaticCall::class: + // fetching a static property, e.g. `self::$my_property` + case StaticPropertyFetch::class: + if (!is_a($node->class, Name::class) || $node->class->toString() !== 'self') { + return []; + } + break; + // `self` as a type (for a property, argument, or return type) + case Name::class: + // Trait can use `self` for typehinting + if ($scope->isInTrait() || $node->toString() !== 'self') { + return []; + } + break; + // instantiating a new object from `self`, e.g. `new self()` + case New_::class: + if ($node->class->toString() !== 'self') { + return []; + } + break; + default: + return []; + } + + $actualClass = $scope->isInTrait() ? 'self::class' : $scope->getClassReflection()->getName(); + return [ + RuleErrorBuilder::message( + "Can't use keyword 'self'. Use '$actualClass' instead." + )->build() + ]; + } +} diff --git a/tests/PHPStan/KeywordSelfRuleTest.php b/tests/PHPStan/KeywordSelfRuleTest.php new file mode 100644 index 0000000..6651b65 --- /dev/null +++ b/tests/PHPStan/KeywordSelfRuleTest.php @@ -0,0 +1,62 @@ + + */ +class KeywordSelfRuleTest extends RuleTestCase +{ + protected function getRule(): Rule + { + return new KeywordSelfRule(); + } + + public function provideRule() + { + return [ + 'interface' => [ + 'filePaths' => [__DIR__ . '/KeywordSelfRuleTest/TestInterface.php'], + 'errorMessage' => "Can't use keyword 'self'. Use '" . TestInterface::class . "' instead.", + 'errorLines' => [13, 18, 18], + ], + 'class' => [ + 'filePaths' => [__DIR__ . '/KeywordSelfRuleTest/TestClass.php'], + 'errorMessage' => "Can't use keyword 'self'. Use '" . TestClass::class . "' instead.", + 'errorLines' => [9, 11, 16, 16, 18, 20, 21, 21, 25], + ], + 'trait' => [ + 'filePaths' => [__DIR__ . '/KeywordSelfRuleTest/TestTrait.php', __DIR__ . '/KeywordSelfRuleTest/ClassUsesTrait.php'], + 'errorMessage' => "Can't use keyword 'self'. Use 'self::class' instead.", + 'errorLines' => [17, 19, 20, 24], + ], + 'trait no errors' => [ + 'filePaths' => [__DIR__ . '/KeywordSelfRuleTest/TestTrait.php', __DIR__ . '/KeywordSelfRuleTest/ClassUsesTrait.php'], + 'errorMessage' => "Can't use keyword 'self'. Use 'self::class' instead.", + 'errorLines' => [], + ], + ]; + } + + /** + * @dataProvider provideRule + */ + public function testRule(array $filePaths, string $errorMessage, array $errorLines): void + { + $errors = []; + foreach ($errorLines as $line) { + $errors[] = [$errorMessage, $line]; + } + $this->analyse($filePaths, $errors); + } +} diff --git a/tests/PHPStan/KeywordSelfRuleTest/Anothertest.php b/tests/PHPStan/KeywordSelfRuleTest/Anothertest.php new file mode 100644 index 0000000..e69de29 diff --git a/tests/PHPStan/KeywordSelfRuleTest/ClassUsesTrait.php b/tests/PHPStan/KeywordSelfRuleTest/ClassUsesTrait.php new file mode 100644 index 0000000..8594e10 --- /dev/null +++ b/tests/PHPStan/KeywordSelfRuleTest/ClassUsesTrait.php @@ -0,0 +1,11 @@ +