From 73c26aa154e609d2f5d385c15e0929e672aed66e Mon Sep 17 00:00:00 2001 From: Carlos A Sastre Date: Sun, 13 Aug 2023 08:54:42 +0200 Subject: [PATCH 1/4] Add shouldBeReadonly assertion --- extension.neon | 8 ++- .../ShouldBeReadonly/IsReadonlyRule.php | 17 ++++++ .../ShouldBeReadonly/ShouldBeReadonly.php | 54 +++++++++++++++++++ .../Declaration/ReadonlyExtractor.php | 25 +++++++++ src/Test/Builder/AssertionStep.php | 8 +++ .../ShouldBeReadonly/ReadonlyClassTest.php | 47 ++++++++++++++++ 6 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 src/Rule/Assertion/Declaration/ShouldBeReadonly/IsReadonlyRule.php create mode 100644 src/Rule/Assertion/Declaration/ShouldBeReadonly/ShouldBeReadonly.php create mode 100644 src/Rule/Extractor/Declaration/ReadonlyExtractor.php create mode 100644 tests/unit/rules/ShouldBeReadonly/ReadonlyClassTest.php diff --git a/extension.neon b/extension.neon index 09e83bc3..9616147d 100644 --- a/extension.neon +++ b/extension.neon @@ -16,7 +16,13 @@ services: show_rule_names: %phpat.show_rule_names%, ) - # # # # # DECLARATION RULES # # # # # + # # # # # DECLARATION RULES # # # # # + + # ShouldBeReadonly rules + - + class: PHPat\Rule\Assertion\Declaration\ShouldBeReadonly\IsReadonlyRule + tags: + - phpstan.rules.rule # ShouldBeAbstract rules - diff --git a/src/Rule/Assertion/Declaration/ShouldBeReadonly/IsReadonlyRule.php b/src/Rule/Assertion/Declaration/ShouldBeReadonly/IsReadonlyRule.php new file mode 100644 index 00000000..f6d2e5f1 --- /dev/null +++ b/src/Rule/Assertion/Declaration/ShouldBeReadonly/IsReadonlyRule.php @@ -0,0 +1,17 @@ + + */ +class IsReadonlyRule extends ShouldBeReadonly implements Rule +{ + use ReadonlyExtractor; +} diff --git a/src/Rule/Assertion/Declaration/ShouldBeReadonly/ShouldBeReadonly.php b/src/Rule/Assertion/Declaration/ShouldBeReadonly/ShouldBeReadonly.php new file mode 100644 index 00000000..1f6b891d --- /dev/null +++ b/src/Rule/Assertion/Declaration/ShouldBeReadonly/ShouldBeReadonly.php @@ -0,0 +1,54 @@ + + */ + protected function applyValidation(string $ruleName, ClassReflection $subject, bool $meetsDeclaration, array $tips): array + { + return $this->applyShould($ruleName, $subject, $meetsDeclaration, $tips); + } + + protected function getMessage(string $ruleName, string $subject): string + { + return $this->prepareMessage( + $ruleName, + sprintf('%s should be readonly', $subject) + ); + } +} diff --git a/src/Rule/Extractor/Declaration/ReadonlyExtractor.php b/src/Rule/Extractor/Declaration/ReadonlyExtractor.php new file mode 100644 index 00000000..2893d9d7 --- /dev/null +++ b/src/Rule/Extractor/Declaration/ReadonlyExtractor.php @@ -0,0 +1,25 @@ +getClassReflection()->isReadOnly(); + } +} diff --git a/src/Test/Builder/AssertionStep.php b/src/Test/Builder/AssertionStep.php index c2b38e83..cc19fb8a 100644 --- a/src/Test/Builder/AssertionStep.php +++ b/src/Test/Builder/AssertionStep.php @@ -6,6 +6,7 @@ use PHPat\Rule\Assertion\Declaration\ShouldBeAbstract\ShouldBeAbstract; use PHPat\Rule\Assertion\Declaration\ShouldBeFinal\ShouldBeFinal; +use PHPat\Rule\Assertion\Declaration\ShouldBeReadonly\ShouldBeReadonly; use PHPat\Rule\Assertion\Declaration\ShouldNotBeAbstract\ShouldNotBeAbstract; use PHPat\Rule\Assertion\Declaration\ShouldNotBeFinal\ShouldNotBeFinal; use PHPat\Rule\Assertion\Relation\CanOnlyDepend\CanOnlyDepend; @@ -32,6 +33,13 @@ public function shouldNotBeAbstract(): Rule return new BuildStep($this->rule); } + public function shouldBeReadonly(): Rule + { + $this->rule->assertion = ShouldBeReadonly::class; + + return new BuildStep($this->rule); + } + public function shouldBeFinal(): Rule { $this->rule->assertion = ShouldBeFinal::class; diff --git a/tests/unit/rules/ShouldBeReadonly/ReadonlyClassTest.php b/tests/unit/rules/ShouldBeReadonly/ReadonlyClassTest.php new file mode 100644 index 00000000..b115f7c1 --- /dev/null +++ b/tests/unit/rules/ShouldBeReadonly/ReadonlyClassTest.php @@ -0,0 +1,47 @@ + + */ +class ReadonlyClassTest extends RuleTestCase +{ + public const RULE_NAME = 'test_FixtureClassShouldBeReadonly'; + public function testRule(): void + { + $this->analyse(['tests/fixtures/FixtureClass.php'], [ + [sprintf('%s should be readonly', FixtureClass::class), 31], + ]); + } + + protected function getRule(): Rule + { + $testParser = FakeTestParser::create( + self::RULE_NAME, + ShouldBeReadonly::class, + [new Classname(FixtureClass::class, false)], + [] + ); + + return new IsReadonlyRule( + new StatementBuilderFactory($testParser), + new Configuration(false, true, false), + $this->createReflectionProvider(), + self::getContainer()->getByType(FileTypeMapper::class) + ); + } +} From 77a02cf6838bdd7edcbf16cc4f054d07e05c6bec Mon Sep 17 00:00:00 2001 From: Carlos A Sastre Date: Mon, 14 Aug 2023 17:44:50 +0200 Subject: [PATCH 2/4] Ignore psalm non-empty-string warning --- ci/psalm.xml | 4 ++++ src/Selector/SelectorPrimitive.php | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/ci/psalm.xml b/ci/psalm.xml index 3a73045f..352a607c 100644 --- a/ci/psalm.xml +++ b/ci/psalm.xml @@ -51,5 +51,9 @@ + + + + diff --git a/src/Selector/SelectorPrimitive.php b/src/Selector/SelectorPrimitive.php index 89d2b6f4..41aea72c 100644 --- a/src/Selector/SelectorPrimitive.php +++ b/src/Selector/SelectorPrimitive.php @@ -52,21 +52,33 @@ public static function attribute(): IsAttribute return new IsAttribute(); } + /** + * @param class-string|non-empty-string $namespace + */ public static function namespace(string $namespace, bool $regex = false): ClassNamespace { return new ClassNamespace($namespace, $regex); } + /** + * @param class-string|non-empty-string $fqcn + */ public static function classname(string $fqcn, bool $regex = false): Classname { return new Classname($fqcn, $regex); } + /** + * @param class-string|non-empty-string $fqcn + */ public static function implements(string $fqcn, bool $regex = false): ClassImplements { return new ClassImplements($fqcn, $regex); } + /** + * @param class-string|non-empty-string $fqcn + */ public static function extends(string $fqcn, bool $regex = false): ClassExtends { return new ClassExtends($fqcn, $regex); From 26353f54d464681ce267e5a79c52f9cef60f4856 Mon Sep 17 00:00:00 2001 From: Carlos A Sastre Date: Mon, 14 Aug 2023 17:49:50 +0200 Subject: [PATCH 3/4] Add Selector::readonly() --- doc/SELECTORS.md | 3 +++ src/Selector/IsReadonly.php | 20 ++++++++++++++++++++ src/Selector/SelectorPrimitive.php | 5 +++++ 3 files changed, 28 insertions(+) create mode 100644 src/Selector/IsReadonly.php diff --git a/doc/SELECTORS.md b/doc/SELECTORS.md index e68d629d..7f4e0c51 100644 --- a/doc/SELECTORS.md +++ b/doc/SELECTORS.md @@ -38,3 +38,6 @@ Select all abstract classes. ### `Selector::final()` Select all final classes. + +### `Selector::readonly()` +Select all readonly classes. diff --git a/src/Selector/IsReadonly.php b/src/Selector/IsReadonly.php new file mode 100644 index 00000000..ef9865d6 --- /dev/null +++ b/src/Selector/IsReadonly.php @@ -0,0 +1,20 @@ +isReadOnly(); + } +} diff --git a/src/Selector/SelectorPrimitive.php b/src/Selector/SelectorPrimitive.php index 41aea72c..1d73eb25 100644 --- a/src/Selector/SelectorPrimitive.php +++ b/src/Selector/SelectorPrimitive.php @@ -34,6 +34,11 @@ public static function final(): IsFinal return new IsFinal(); } + public static function readonly(): IsReadonly + { + return new IsReadonly(); + } + /** * @deprecated use Selector::NOT(Selector::final()) */ From 2f5f676a63aab5ae12137145349d1e104b1230c4 Mon Sep 17 00:00:00 2001 From: Carlos A Sastre Date: Mon, 14 Aug 2023 17:50:03 +0200 Subject: [PATCH 4/4] Add Selector::attribute() --- doc/SELECTORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/SELECTORS.md b/doc/SELECTORS.md index 7f4e0c51..90fb2817 100644 --- a/doc/SELECTORS.md +++ b/doc/SELECTORS.md @@ -41,3 +41,6 @@ Select all final classes. ### `Selector::readonly()` Select all readonly classes. + +### `Selector::attribute()` +Select all attribute classes.