Skip to content

Commit

Permalink
Enhancement: Implement NoReturnByReferenceRule
Browse files Browse the repository at this point in the history
  • Loading branch information
localheinz committed Feb 17, 2025
1 parent 4e42c4f commit e1f463e
Show file tree
Hide file tree
Showing 17 changed files with 406 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ For a full diff see [`2.6.1...main`][2.6.1...main].
### Added

- Added `Closures\NoParameterPassedByReferenceRule`, `Functions\NoParameterPassedByReferenceRule`, `Methods\NoParameterPassedByReferenceRule`, which report an error when a closure, a function, or a method has a parameter that is passed by reference ([#911]), by [@localheinz]
- Added `Functions\NoReturnByReferenceRule` and `Methods\NoReturnByReferenceRule`, which report an error when a function or a method returns by reference ([#912]), by [@localheinz]

## [`2.6.1`][2.6.1]

Expand Down Expand Up @@ -604,6 +605,7 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
[#897]: https://github.com/ergebnis/phpstan-rules/pull/897
[#902]: https://github.com/ergebnis/phpstan-rules/pull/902
[#911]: https://github.com/ergebnis/phpstan-rules/pull/911
[#912]: https://github.com/ergebnis/phpstan-rules/pull/912

[@cosmastech]: https://github.com/cosmastech
[@enumag]: https://github.com/enumag
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ This package provides the following rules for use with [`phpstan/phpstan`](https
- [`Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterpassedbyreferencerule)
- [`Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterwithnullabletypedeclarationrule)
- [`Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterwithnulldefaultvaluerule)
- [`Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule`](https://github.com/ergebnis/phpstan-rules#functionsnoreturnbyreferencerule)
- [`Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule`](https://github.com/ergebnis/phpstan-rules#methodsfinalinabstractclassrule)
- [`Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#methodsnoconstructorparameterwithdefaultvaluerule)
- [`Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnonullablereturntypedeclarationrule)
- [`Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterpassedbyreferencerule)
- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithcontainertypedeclarationrule)
- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithnullabletypedeclarationrule)
- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithnulldefaultvaluerule)
- [`Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule`](https://github.com/ergebnis/phpstan-rules#methodsnoreturnbyreferencerule)
- [`Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule`](https://github.com/ergebnis/phpstan-rules#methodsprivateinfinalclassrule)
- [`Ergebnis\PHPStan\Rules\Statements\NoSwitchRule`](https://github.com/ergebnis/phpstan-rules#statementsnoswitchrule)

Expand Down Expand Up @@ -372,6 +374,21 @@ parameters:
enabled: false
```

#### `Functions\NoReturnByReferenceRule`

This rule reports an error when a function [returns by reference](https://www.php.net/manual/en/language.references.return.php).

##### Disabling the rule

You can set the `enabled` parameter to `false` to disable this rule.

```neon
parameters:
ergebnis:
noReturnByReference:
enabled: false
```

### Methods

#### `Methods\FinalInAbstractClassRule`
Expand Down Expand Up @@ -540,6 +557,21 @@ parameters:
enabled: false
```

#### `Functions\NoReturnByReferenceRule`

This rule reports an error when a method [returns by reference](https://www.php.net/manual/en/language.references.return.php).

##### Disabling the rule

You can set the `enabled` parameter to `false` to disable this rule.

```neon
parameters:
ergebnis:
noReturnByReference:
enabled: false
```

#### `Methods\PrivateInFinalClassRule`

This rule reports an error when a method in a `final` class is `protected` but could be `private`.
Expand Down
15 changes: 15 additions & 0 deletions rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ conditionalTags:
phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled%
Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule:
phpstan.rules.rule: %ergebnis.noParameterWithNullDefaultValue.enabled%
Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule:
phpstan.rules.rule: %ergebnis.noReturnByReference.enabled%
Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule:
phpstan.rules.rule: %ergebnis.finalInAbstractClass.enabled%
Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule:
Expand All @@ -43,6 +45,8 @@ conditionalTags:
phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled%
Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule:
phpstan.rules.rule: %ergebnis.noParameterWithNullDefaultValue.enabled%
Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule:
phpstan.rules.rule: %ergebnis.noReturnByReference.enabled%
Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule:
phpstan.rules.rule: %ergebnis.privateInFinalClass.enabled%
Ergebnis\PHPStan\Rules\Statements\NoSwitchRule:
Expand Down Expand Up @@ -84,6 +88,8 @@ parameters:
enabled: true
noParameterWithNullDefaultValue:
enabled: true
noReturnByReference:
enabled: true
noSwitch:
enabled: true
privateInFinalClass:
Expand Down Expand Up @@ -140,6 +146,9 @@ parametersSchema:
noParameterWithNullDefaultValue: structure([
enabled: bool(),
])
noReturnByReference: structure([
enabled: bool(),
])
noSwitch: structure([
enabled: bool(),
])
Expand Down Expand Up @@ -205,6 +214,9 @@ services:
-
class: Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule

-
class: Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule

-
class: Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule

Expand All @@ -229,6 +241,9 @@ services:
-
class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule

-
class: Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule

-
class: Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule

Expand Down
5 changes: 5 additions & 0 deletions src/ErrorIdentifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ public static function noNullableReturnTypeDeclaration(): self
return new self('noNullableReturnTypeDeclaration');
}

public static function noReturnByReference(): self
{
return new self('noReturnByReference');
}

public static function noSwitch(): self
{
return new self('noSwitch');
Expand Down
50 changes: 50 additions & 0 deletions src/Functions/NoReturnByReferenceRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

/**
* Copyright (c) 2018-2025 Andreas Möller
*
* For the full copyright and license information, please view
* the LICENSE.md file that was distributed with this source code.
*
* @see https://github.com/ergebnis/phpstan-rules
*/

namespace Ergebnis\PHPStan\Rules\Functions;

use Ergebnis\PHPStan\Rules\ErrorIdentifier;
use PhpParser\Node;
use PHPStan\Analyser;
use PHPStan\Rules;

/**
* @implements Rules\Rule<Node\Stmt\Function_>
*/
final class NoReturnByReferenceRule implements Rules\Rule
{
public function getNodeType(): string
{
return Node\Stmt\Function_::class;
}

public function processNode(
Node $node,
Analyser\Scope $scope
): array {
if (false === $node->byRef) {
return [];
}

$message = \sprintf(
'Function %s() returns by reference.',
$node->namespacedName,
);

return [
Rules\RuleErrorBuilder::message($message)
->identifier(ErrorIdentifier::noReturnByReference()->toString())
->build(),
];
}
}
72 changes: 72 additions & 0 deletions src/Methods/NoReturnByReferenceRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

/**
* Copyright (c) 2018-2025 Andreas Möller
*
* For the full copyright and license information, please view
* the LICENSE.md file that was distributed with this source code.
*
* @see https://github.com/ergebnis/phpstan-rules
*/

namespace Ergebnis\PHPStan\Rules\Methods;

use Ergebnis\PHPStan\Rules\ErrorIdentifier;
use PhpParser\Node;
use PHPStan\Analyser;
use PHPStan\Reflection;
use PHPStan\Rules;

/**
* @implements Rules\Rule<Node\Stmt\ClassMethod>
*/
final class NoReturnByReferenceRule implements Rules\Rule
{
public function getNodeType(): string
{
return Node\Stmt\ClassMethod::class;
}

public function processNode(
Node $node,
Analyser\Scope $scope
): array {
if (false === $node->byRef) {
return [];
}

$methodName = $node->name->toString();

/** @var Reflection\ClassReflection $classReflection */
$classReflection = $scope->getClassReflection();

if ($classReflection->isAnonymous()) {
$message = \sprintf(
'Method %s() in anonymous class returns by reference.',
$methodName,
);

return [
Rules\RuleErrorBuilder::message($message)
->identifier(ErrorIdentifier::noReturnByReference()->toString())
->build(),
];
}

$className = $classReflection->getName();

$message = \sprintf(
'Method %s::%s() returns by reference.',
$className,
$methodName,
);

return [
Rules\RuleErrorBuilder::message($message)
->identifier(ErrorIdentifier::noReturnByReference()->toString())
->build(),
];
}
}
14 changes: 14 additions & 0 deletions test/Fixture/Functions/NoReturnByReferenceRule/script.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Functions\NoReturnByReferenceRule;

function foo(): void
{
}

function &bar($bar)
{
return $bar;
}
13 changes: 13 additions & 0 deletions test/Fixture/Methods/NoReturnByReferenceRule/MethodInClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoReturnByReferenceRule;

final class MethodInClass
{
public function foo($bar)
{
return $bar;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoReturnByReferenceRule;

final class MethodInClassReturningByReference
{
public function &foo($bar)
{
return $bar;
}
}
10 changes: 10 additions & 0 deletions test/Fixture/Methods/NoReturnByReferenceRule/MethodInInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoReturnByReferenceRule;

interface MethodInInterface
{
public function foo($bar);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoReturnByReferenceRule;

interface MethodInInterfaceReturningByReference
{
public function &foo($bar);
}
13 changes: 13 additions & 0 deletions test/Fixture/Methods/NoReturnByReferenceRule/MethodInTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoReturnByReferenceRule;

trait MethodInTrait
{
public function foo($bar)
{
return $bar;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoReturnByReferenceRule;

trait MethodInTraitReturningByReference
{
public function &foo($bar)
{
return $bar;
}
}
25 changes: 25 additions & 0 deletions test/Fixture/Methods/NoReturnByReferenceRule/script.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\Methods\NoReturnByReferenceRule;

$foo = new class() {
public function foo(): void
{
}
};

$bar = new class() {
public function foo($bar)
{
return $bar;
}
};

$baz = new class() {
public function &foo($bar)
{
return $bar;
}
};
Loading

0 comments on commit e1f463e

Please sign in to comment.