Skip to content

Commit 0b700c1

Browse files
Add support for PHP 8.5 #[\NoDiscard] on functions
1 parent fbb9cb6 commit 0b700c1

File tree

3 files changed

+107
-0
lines changed

3 files changed

+107
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Functions;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\DependencyInjection\RegisteredRule;
8+
use PHPStan\Reflection\ReflectionProvider;
9+
use PHPStan\Rules\Rule;
10+
use PHPStan\Rules\RuleErrorBuilder;
11+
use function sprintf;
12+
13+
/**
14+
* @implements Rule<Node\Stmt\Expression>
15+
*/
16+
#[RegisteredRule(level: 4)]
17+
final class CallToFunctionStatementWithNoDiscardRule implements Rule
18+
{
19+
20+
public function __construct(private ReflectionProvider $reflectionProvider)
21+
{
22+
}
23+
24+
public function getNodeType(): string
25+
{
26+
return Node\Stmt\Expression::class;
27+
}
28+
29+
public function processNode(Node $node, Scope $scope): array
30+
{
31+
if (!$node->expr instanceof Node\Expr\FuncCall) {
32+
return [];
33+
}
34+
35+
$funcCall = $node->expr;
36+
if (!($funcCall->name instanceof Node\Name)) {
37+
return [];
38+
}
39+
40+
if (!$this->reflectionProvider->hasFunction($funcCall->name, $scope)) {
41+
return [];
42+
}
43+
44+
$function = $this->reflectionProvider->getFunction($funcCall->name, $scope);
45+
46+
$attributes = $function->getAttributes();
47+
$hasNoDiscard = false;
48+
foreach ($attributes as $attrib) {
49+
if ($attrib->getName() === 'NoDiscard') {
50+
$hasNoDiscard = true;
51+
break;
52+
}
53+
}
54+
if (!$hasNoDiscard) {
55+
return [];
56+
}
57+
58+
return [
59+
RuleErrorBuilder::message(sprintf(
60+
'Call to function %s() on a separate line discards return value.',
61+
$function->getName(),
62+
))->identifier('function.resultDiscarded')->build(),
63+
];
64+
}
65+
66+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Functions;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<CallToFunctionStatementWithNoDiscardRule>
10+
*/
11+
class CallToFunctionStatementWithNoDiscardRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new CallToFunctionStatementWithNoDiscardRule(self::createReflectionProvider());
17+
}
18+
19+
public function testRule(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/function-call-statement-result-discarded.php'], [
22+
[
23+
'Call to function FunctionCallStatementResultDiscarded\withSideEffects() on a separate line discards return value.',
24+
11,
25+
],
26+
]);
27+
28+
}
29+
30+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace FunctionCallStatementResultDiscarded;
4+
5+
#[\NoDiscard]
6+
function withSideEffects(): int {
7+
echo __FUNCTION__ . "\n";
8+
return 1;
9+
}
10+
11+
withSideEffects();

0 commit comments

Comments
 (0)