Skip to content

Commit 5d76184

Browse files
committed
Detect void parameter typehint
1 parent b8bee3b commit 5d76184

6 files changed

Lines changed: 81 additions & 3 deletions

src/Rules/FunctionDefinitionCheck.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PHPStan\Reflection\ReflectionProvider;
1919
use PHPStan\Type\NonexistentParentClassType;
2020
use PHPStan\Type\VerbosityLevel;
21+
use PHPStan\Type\VoidType;
2122

2223
class FunctionDefinitionCheck
2324
{
@@ -87,12 +88,15 @@ public function checkAnonymousFunction(
8788
if ($param->type === null) {
8889
continue;
8990
}
91+
if (!$param->var instanceof Variable || !is_string($param->var->name)) {
92+
throw new \PHPStan\ShouldNotHappenException();
93+
}
9094
$type = $scope->getFunctionType($param->type, false, $param->variadic);
95+
if ($type instanceof VoidType) {
96+
$errors[] = RuleErrorBuilder::message(sprintf($parameterMessage, $param->var->name, 'void'))->line($param->type->getLine())->nonIgnorable()->build();
97+
}
9198
foreach ($type->getReferencedClasses() as $class) {
9299
if (!$this->reflectionProvider->hasClass($class) || $this->reflectionProvider->getClass($class)->isTrait()) {
93-
if (!$param->var instanceof Variable || !is_string($param->var->name)) {
94-
throw new \PHPStan\ShouldNotHappenException();
95-
}
96100
$errors[] = RuleErrorBuilder::message(sprintf($parameterMessage, $param->var->name, $class))->line($param->type->getLine())->build();
97101
} elseif ($this->checkClassCaseSensitivity) {
98102
$errors = array_merge(
@@ -178,6 +182,16 @@ private function checkParametersAcceptor(
178182

179183
return $parameterNode;
180184
};
185+
if (
186+
$parameter instanceof ParameterReflectionWithPhpDocs
187+
&& $parameter->getNativeType() instanceof VoidType
188+
) {
189+
$parameterVar = $parameterNodeCallback()->var;
190+
if (!$parameterVar instanceof Variable || !is_string($parameterVar->name)) {
191+
throw new \PHPStan\ShouldNotHappenException();
192+
}
193+
$errors[] = RuleErrorBuilder::message(sprintf($parameterMessage, $parameterVar->name, 'void'))->line($parameterNodeCallback()->getLine())->nonIgnorable()->build();
194+
}
181195
foreach ($referencedClasses as $class) {
182196
if ($this->reflectionProvider->hasClass($class) && !$this->reflectionProvider->getClass($class)->isTrait()) {
183197
continue;

tests/PHPStan/Rules/Functions/ExistingClassesInClosureTypehintsRuleTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,17 @@ public function testValidTypehintPhp72(): void
6969
$this->analyse([__DIR__ . '/data/closure-7.2-typehints.php'], []);
7070
}
7171

72+
public function testVoidParameterTypehint(): void
73+
{
74+
if (!self::$useStaticReflectionProvider) {
75+
$this->markTestSkipped('Test requires static reflection');
76+
}
77+
$this->analyse([__DIR__ . '/data/void-parameter-typehint.php'], [
78+
[
79+
'Parameter $param of anonymous function has invalid typehint type void.',
80+
5,
81+
],
82+
]);
83+
}
84+
7285
}

tests/PHPStan/Rules/Functions/ExistingClassesInTypehintsRuleTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,17 @@ public function testWithoutNamespace(): void
143143
]);
144144
}
145145

146+
public function testVoidParameterTypehint(): void
147+
{
148+
if (!self::$useStaticReflectionProvider) {
149+
$this->markTestSkipped('Test requires static reflection');
150+
}
151+
$this->analyse([__DIR__ . '/data/void-parameter-typehint.php'], [
152+
[
153+
'Parameter $param of function VoidParameterTypehint\doFoo() has invalid typehint type void.',
154+
9,
155+
],
156+
]);
157+
}
158+
146159
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace VoidParameterTypehint;
4+
5+
function (void $param): int {
6+
return 1;
7+
};
8+
9+
function doFoo(void $param): int
10+
{
11+
return 1;
12+
}

tests/PHPStan/Rules/Methods/ExistingClassesInTypehintsRuleTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,17 @@ public function testExistingClassInIterableTypehint(): void
128128
]);
129129
}
130130

131+
public function testVoidParameterTypehint(): void
132+
{
133+
if (!self::$useStaticReflectionProvider) {
134+
$this->markTestSkipped('Test requires static reflection');
135+
}
136+
$this->analyse([__DIR__ . '/data/void-parameter-typehint.php'], [
137+
[
138+
'Parameter $param of method VoidParameterTypehintMethod\Foo::doFoo() has invalid typehint type void.',
139+
8,
140+
],
141+
]);
142+
}
143+
131144
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace VoidParameterTypehintMethod;
4+
5+
class Foo
6+
{
7+
8+
public function doFoo(void $param): int
9+
{
10+
return 1;
11+
}
12+
13+
}

0 commit comments

Comments
 (0)