Skip to content

Commit 9697760

Browse files
paulbalandanondrejmirtes
authored andcommitted
Add InvalidCallablePropertyTypeRule
1 parent 8d460ac commit 9697760

File tree

5 files changed

+139
-0
lines changed

5 files changed

+139
-0
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ lint:
4040
--exclude tests/PHPStan/Rules/Functions/data/arrow-function-nullsafe-by-ref.php \
4141
--exclude tests/PHPStan/Levels/data/namedArguments.php \
4242
--exclude tests/PHPStan/Rules/Keywords/data/continue-break.php \
43+
--exclude tests/PHPStan/Rules/Properties/data/invalid-callable-property-type.php \
4344
--exclude tests/PHPStan/Rules/Properties/data/properties-in-interface.php \
4445
--exclude tests/PHPStan/Rules/Properties/data/read-only-property.php \
4546
--exclude tests/PHPStan/Rules/Properties/data/read-only-property-phpdoc-and-native.php \

conf/config.level0.neon

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ rules:
8888
- PHPStan\Rules\Operators\InvalidAssignVarRule
8989
- PHPStan\Rules\Properties\AccessPropertiesInAssignRule
9090
- PHPStan\Rules\Properties\AccessStaticPropertiesInAssignRule
91+
- PHPStan\Rules\Properties\InvalidCallablePropertyTypeRule
9192
- PHPStan\Rules\Properties\MissingReadOnlyPropertyAssignRule
9293
- PHPStan\Rules\Properties\PropertiesInInterfaceRule
9394
- PHPStan\Rules\Properties\PropertyAttributesRule
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Properties;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Node\ClassPropertyNode;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\Type\CallableType;
11+
use PHPStan\Type\IntersectionType;
12+
use PHPStan\Type\Type;
13+
use PHPStan\Type\TypeTraverser;
14+
use PHPStan\Type\UnionType;
15+
use function sprintf;
16+
17+
/**
18+
* @implements Rule<ClassPropertyNode>
19+
*/
20+
class InvalidCallablePropertyTypeRule implements Rule
21+
{
22+
23+
public function getNodeType(): string
24+
{
25+
return ClassPropertyNode::class;
26+
}
27+
28+
public function processNode(Node $node, Scope $scope): array
29+
{
30+
$classReflection = $node->getClassReflection();
31+
$propertyReflection = $classReflection->getNativeProperty($node->getName());
32+
33+
if (!$propertyReflection->hasNativeType()) {
34+
return [];
35+
}
36+
37+
$nativeType = $propertyReflection->getNativeType();
38+
$callableTypes = [];
39+
40+
TypeTraverser::map($nativeType, static function (Type $type, callable $traverse) use (&$callableTypes): Type {
41+
if ($type instanceof UnionType || $type instanceof IntersectionType) {
42+
return $traverse($type);
43+
}
44+
45+
if ($type instanceof CallableType) {
46+
$callableTypes[] = $type;
47+
}
48+
49+
return $type;
50+
});
51+
52+
if ($callableTypes === []) {
53+
return [];
54+
}
55+
56+
return [
57+
RuleErrorBuilder::message(sprintf(
58+
'Property %s::$%s cannot have callable in its type declaration.',
59+
$classReflection->getDisplayName(),
60+
$node->getName(),
61+
))->identifier('property.callableType')->nonIgnorable()->build(),
62+
];
63+
}
64+
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Properties;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<InvalidCallablePropertyTypeRule>
10+
*/
11+
class InvalidCallablePropertyTypeRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new InvalidCallablePropertyTypeRule();
17+
}
18+
19+
public function testRule(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/invalid-callable-property-type.php'], [
22+
[
23+
'Property InvalidCallablePropertyType\HelloWorld::$a cannot have callable in its type declaration.',
24+
9,
25+
],
26+
[
27+
'Property InvalidCallablePropertyType\HelloWorld::$b cannot have callable in its type declaration.',
28+
12,
29+
],
30+
[
31+
'Property InvalidCallablePropertyType\HelloWorld::$c cannot have callable in its type declaration.',
32+
15,
33+
],
34+
[
35+
'Property InvalidCallablePropertyType\HelloWorld::$callback cannot have callable in its type declaration.',
36+
23,
37+
],
38+
]);
39+
}
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace InvalidCallablePropertyType;
4+
5+
class HelloWorld
6+
{
7+
8+
/** @var callable(): void */
9+
public callable $a;
10+
11+
/** @var callable(): void|null */
12+
public ?callable $b;
13+
14+
/** @var callable(): void|string */
15+
public callable|string $c;
16+
17+
/**
18+
* @param \Closure(): void $closure
19+
* @param callable(): void $callback
20+
*/
21+
public function __construct(
22+
\Closure $closure,
23+
public callable $callback
24+
)
25+
{
26+
$this->a = $closure;
27+
$this->b = $closure;
28+
$this->c = $closure;
29+
}
30+
31+
}

0 commit comments

Comments
 (0)