Skip to content

Commit 21c50e3

Browse files
committed
Detect unused constructor method calls
1 parent 25d5715 commit 21c50e3

4 files changed

+107
-0
lines changed

conf/config.level4.neon

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ rules:
88
- PHPStan\Rules\DeadCode\UnreachableStatementRule
99
- PHPStan\Rules\Exceptions\DeadCatchRule
1010
- PHPStan\Rules\Functions\CallToFunctionStamentWithoutSideEffectsRule
11+
- PHPStan\Rules\Methods\CallToConstructorStatementWithoutSideEffectsRule
1112
- PHPStan\Rules\Methods\CallToMethodStamentWithoutSideEffectsRule
1213
- PHPStan\Rules\Methods\CallToStaticMethodStamentWithoutSideEffectsRule
1314
- PHPStan\Rules\TooWideTypehints\TooWideArrowFunctionReturnTypehintRule
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Methods;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\ReflectionProvider;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
11+
/**
12+
* @implements \PHPStan\Rules\Rule<\PhpParser\Node\Stmt\Expression>
13+
*/
14+
class CallToConstructorStatementWithoutSideEffectsRule implements Rule
15+
{
16+
17+
private ReflectionProvider $reflectionProvider;
18+
19+
public function __construct(ReflectionProvider $reflectionProvider)
20+
{
21+
$this->reflectionProvider = $reflectionProvider;
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\New_) {
32+
return [];
33+
}
34+
35+
$instantiation = $node->expr;
36+
if (!$instantiation->class instanceof Node\Name) {
37+
return [];
38+
}
39+
40+
$className = $scope->resolveName($instantiation->class);
41+
if (!$this->reflectionProvider->hasClass($className)) {
42+
return [];
43+
}
44+
45+
$classReflection = $this->reflectionProvider->getClass($className);
46+
if (!$classReflection->hasConstructor()) {
47+
return [];
48+
}
49+
50+
$constructor = $classReflection->getConstructor();
51+
if ($constructor->hasSideEffects()->no()) {
52+
return [
53+
RuleErrorBuilder::message(sprintf(
54+
'Call to %s::%s() on a separate line has no effect.',
55+
$classReflection->getDisplayName(),
56+
$constructor->getName()
57+
))->build(),
58+
];
59+
}
60+
61+
return [];
62+
}
63+
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Methods;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<CallToConstructorStatementWithoutSideEffectsRule>
10+
*/
11+
class CallToConstructorStatementWithoutSideEffectsRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new CallToConstructorStatementWithoutSideEffectsRule($this->createReflectionProvider());
17+
}
18+
19+
public function testRule(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/constructor-statement-no-side-effects.php'], [
22+
[
23+
'Call to Exception::__construct() on a separate line has no effect.',
24+
6,
25+
],
26+
]);
27+
}
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace ConstructorStatementNoSideEffects;
4+
5+
function () {
6+
new \Exception();
7+
throw new \Exception();
8+
};
9+
10+
function () {
11+
new \PDOStatement();
12+
new \stdClass();
13+
};

0 commit comments

Comments
 (0)