From 3b45950e894d2355226d53508aa78eb31b0df562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=B6ller?= Date: Tue, 18 Feb 2025 11:58:04 +0100 Subject: [PATCH] Enhancement: Implement NoAssignByReferenceRule --- CHANGELOG.md | 2 + README.md | 16 ++++++ composer-require-checker.json | 1 + rules.neon | 10 ++++ src/ErrorIdentifier.php | 5 ++ src/Expressions/NoAssignByReferenceRule.php | 41 ++++++++++++++++ .../NoAssignByReferenceRule/script.php | 11 +++++ .../NoAssignByReferenceRuleTest.php | 49 +++++++++++++++++++ test/Unit/ErrorIdentifierTest.php | 7 +++ 9 files changed, 142 insertions(+) create mode 100644 src/Expressions/NoAssignByReferenceRule.php create mode 100644 test/Fixture/Expressions/NoAssignByReferenceRule/script.php create mode 100644 test/Integration/Expressions/NoAssignByReferenceRuleTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d2a4200..9bcd0287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ For a full diff see [`2.7.0...main`][2.7.0...main]. ### Added - Added `allRules` parameter to allow disabling and enabling all rules ([#913]), by [@localheinz] +- Added `Expressions\NoAssignByReferenceRule`, which reports an error when a variable is assigned by reference ([#914]), by [@localheinz] ## [`2.7.0`][2.7.0] @@ -617,6 +618,7 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0]. [#911]: https://github.com/ergebnis/phpstan-rules/pull/911 [#912]: https://github.com/ergebnis/phpstan-rules/pull/912 [#913]: https://github.com/ergebnis/phpstan-rules/pull/913 +[#914]: https://github.com/ergebnis/phpstan-rules/pull/914 [@cosmastech]: https://github.com/cosmastech [@enumag]: https://github.com/enumag diff --git a/README.md b/README.md index 3f77ae16..8296743c 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ This package provides the following rules for use with [`phpstan/phpstan`](https - [`Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterpassedbyreferencerule) - [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnullabletypedeclarationrule) - [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnulldefaultvaluerule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoassignbyreferencerule) - [`Ergebnis\PHPStan\Rules\Expressions\NoCompactRule`](https://github.com/ergebnis/phpstan-rules#expressionsnocompactrule) - [`Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoerrorsuppressionrule) - [`Ergebnis\PHPStan\Rules\Expressions\NoEvalRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoevalrule) @@ -235,6 +236,21 @@ parameters: ### Expressions +#### `Expressions\NoAssignByReferenceRule` + +This rule reports an error when [a variable is assigned by reference](https://www.php.net/manual/en/language.references.whatdo.php#language.references.whatdo.assign). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noAssignByReference: + enabled: false +``` + #### `Expressions\NoCompactRule` This rule reports an error when the function [`compact()`](https://www.php.net/compact) is used. diff --git a/composer-require-checker.json b/composer-require-checker.json index 1f7f3d81..e3f6e5e9 100644 --- a/composer-require-checker.json +++ b/composer-require-checker.json @@ -7,6 +7,7 @@ "null", "PhpParser\\Comment\\Doc", "PhpParser\\Node", + "PhpParser\\Node\\Expr\\AssignRef", "PhpParser\\Node\\Expr\\Closure", "PhpParser\\Node\\Expr\\ConstFetch", "PhpParser\\Node\\Expr\\ErrorSuppress", diff --git a/rules.neon b/rules.neon index 3bc55fea..a81a7e83 100644 --- a/rules.neon +++ b/rules.neon @@ -11,6 +11,8 @@ conditionalTags: phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled% Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule: phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule: + phpstan.rules.rule: %ergebnis.noAssignByReference.enabled% Ergebnis\PHPStan\Rules\Expressions\NoCompactRule: phpstan.rules.rule: %ergebnis.noCompact.enabled% Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule: @@ -63,6 +65,8 @@ parameters: enabled: %ergebnis.allRules% finalInAbstractClass: enabled: %ergebnis.allRules% + noAssignByReference: + enabled: %ergebnis.allRules% noCompact: enabled: %ergebnis.allRules% noConstructorParameterWithDefaultValue: @@ -112,6 +116,9 @@ parametersSchema: finalInAbstractClass: structure([ enabled: bool(), ]) + noAssignByReference: structure([ + enabled: bool(), + ]) noCompact: structure([ enabled: bool(), ]) @@ -189,6 +196,9 @@ services: - class: Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule + - + class: Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule + - class: Ergebnis\PHPStan\Rules\Expressions\NoCompactRule diff --git a/src/ErrorIdentifier.php b/src/ErrorIdentifier.php index 6b4912b8..3fcbb101 100644 --- a/src/ErrorIdentifier.php +++ b/src/ErrorIdentifier.php @@ -50,6 +50,11 @@ public static function noConstructorParameterWithDefaultValue(): self return new self('noConstructorParameterWithDefaultValue'); } + public static function noAssignByReference(): self + { + return new self('noAssignByReference'); + } + public static function noErrorSuppression(): self { return new self('noErrorSuppression'); diff --git a/src/Expressions/NoAssignByReferenceRule.php b/src/Expressions/NoAssignByReferenceRule.php new file mode 100644 index 00000000..ad8cc1ef --- /dev/null +++ b/src/Expressions/NoAssignByReferenceRule.php @@ -0,0 +1,41 @@ + + */ +final class NoAssignByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\AssignRef::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Assign by reference should not be used.') + ->identifier(ErrorIdentifier::noAssignByReference()->toString()) + ->build(), + ]; + } +} diff --git a/test/Fixture/Expressions/NoAssignByReferenceRule/script.php b/test/Fixture/Expressions/NoAssignByReferenceRule/script.php new file mode 100644 index 00000000..dfcbaf41 --- /dev/null +++ b/test/Fixture/Expressions/NoAssignByReferenceRule/script.php @@ -0,0 +1,11 @@ + + */ +final class NoAssignByReferenceRuleTest extends Testing\RuleTestCase +{ + use Test\Util\Helper; + + public function testNoAssignByReferenceRule(): void + { + $this->analyse( + self::phpFilesIn(__DIR__ . '/../../Fixture/Expressions/NoAssignByReferenceRule'), + [ + [ + 'Assign by reference should not be used.', + 11, + ], + ], + ); + } + + protected function getRule(): Rules\Rule + { + return new Expressions\NoAssignByReferenceRule(); + } +} diff --git a/test/Unit/ErrorIdentifierTest.php b/test/Unit/ErrorIdentifierTest.php index be69cb78..17310ae9 100644 --- a/test/Unit/ErrorIdentifierTest.php +++ b/test/Unit/ErrorIdentifierTest.php @@ -42,6 +42,13 @@ public function testFinalReturnsErrorIdentifier(): void self::assertSame('ergebnis.final', $errorIdentifier->toString()); } + public function testNoAssignByReferenceReturnsErrorIdentifier(): void + { + $errorIdentifier = ErrorIdentifier::noAssignByReference(); + + self::assertSame('ergebnis.noAssignByReference', $errorIdentifier->toString()); + } + public function testNoCompactReturnsErrorIdentifier(): void { $errorIdentifier = ErrorIdentifier::noCompact();