Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ parameters:
- stubs/Money/MoneyParser.stub
rules:
- Ibexa\PHPStan\Rules\NoConfigResolverParametersInConstructorRule
- Ibexa\PHPStan\Rules\RequireClosureReturnTypeRule
44 changes: 44 additions & 0 deletions rules/RequireClosureReturnTypeRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\PHPStan\Rules;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

/**
* @implements Rule<Node\Expr>
*/
final class RequireClosureReturnTypeRule implements Rule
{
public function getNodeType(): string
{
return Node\Expr::class;
}

public function processNode(Node $node, Scope $scope): array
{
if (!$node instanceof Node\Expr\Closure && !$node instanceof Node\Expr\ArrowFunction) {
return [];
}

if ($node->returnType === null) {
$nodeType = $node instanceof Node\Expr\Closure ? 'Closure' : 'Arrow function';

return [
RuleErrorBuilder::message(
sprintf('%s is missing a return type declaration', $nodeType)
)->build(),
];
}

return [];
}
}
73 changes: 73 additions & 0 deletions tests/rules/Fixtures/RequireClosureReturnTypeFixture.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\PHPStan\Rules\Fixtures;

final class RequireClosureReturnTypeFixture
{
public function closureWithoutReturnType(): void
{
// Error: Closure without return type
$closure = static function ($x) {
return $x * 2;
};
}

public function closureWithReturnType(): void
{
// OK: Closure has return type
$closure = static function (int $x): int {
return $x * 2;
};
}

public function arrowFunctionWithoutReturnType(): void
{
// Error: Arrow function without return type
$arrow = static fn ($x) => $x * 2;
}

public function arrowFunctionWithReturnType(): void
{
// OK: Arrow function has return type
$arrow = static fn (int $x): int => $x * 2;
}

public function closureWithVoidReturnType(): void
{
// OK: Closure has void return type
$closure = static function (): void {
echo 'Hello';
};
}

public function arrowFunctionWithMixedReturnType(): void
{
// OK: Arrow function has mixed return type
$arrow = static fn ($x): mixed => $x;
}

public function nestedClosuresWithoutReturnType(): void
{
// Error: Outer closure without return type
$outer = static function () {
// Error: Inner closure without return type
return static function ($x) {
return $x * 2;
};
};
}

public function arrayMapWithoutReturnType(): void
{
// Error: Closure without return type
$result = array_map(static function ($x) {
return $x * 2;
}, [1, 2, 3]);
}
}
55 changes: 55 additions & 0 deletions tests/rules/RequireClosureReturnTypeRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\PHPStan\Rules;

use Ibexa\PHPStan\Rules\RequireClosureReturnTypeRule;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends \PHPStan\Testing\RuleTestCase<\Ibexa\PHPStan\Rules\RequireClosureReturnTypeRule>
*/
final class RequireClosureReturnTypeRuleTest extends RuleTestCase
{
protected function getRule(): Rule
{
return new RequireClosureReturnTypeRule();
}

public function testRule(): void
{
$this->analyse(
[
__DIR__ . '/Fixtures/RequireClosureReturnTypeFixture.php',
],
[
[
'Closure is missing a return type declaration',
16,
],
[
'Arrow function is missing a return type declaration',
32,
],
[
'Closure is missing a return type declaration',
58,
],
[
'Closure is missing a return type declaration',
60,
],
[
'Closure is missing a return type declaration',
69,
],
]
);
}
}
Loading