Skip to content

Commit

Permalink
Add InvalidCallablePropertyTypeRule
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalandan authored and ondrejmirtes committed Nov 17, 2023
1 parent 8d460ac commit 9697760
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ lint:
--exclude tests/PHPStan/Rules/Functions/data/arrow-function-nullsafe-by-ref.php \
--exclude tests/PHPStan/Levels/data/namedArguments.php \
--exclude tests/PHPStan/Rules/Keywords/data/continue-break.php \
--exclude tests/PHPStan/Rules/Properties/data/invalid-callable-property-type.php \
--exclude tests/PHPStan/Rules/Properties/data/properties-in-interface.php \
--exclude tests/PHPStan/Rules/Properties/data/read-only-property.php \
--exclude tests/PHPStan/Rules/Properties/data/read-only-property-phpdoc-and-native.php \
Expand Down
1 change: 1 addition & 0 deletions conf/config.level0.neon
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ rules:
- PHPStan\Rules\Operators\InvalidAssignVarRule
- PHPStan\Rules\Properties\AccessPropertiesInAssignRule
- PHPStan\Rules\Properties\AccessStaticPropertiesInAssignRule
- PHPStan\Rules\Properties\InvalidCallablePropertyTypeRule
- PHPStan\Rules\Properties\MissingReadOnlyPropertyAssignRule
- PHPStan\Rules\Properties\PropertiesInInterfaceRule
- PHPStan\Rules\Properties\PropertyAttributesRule
Expand Down
65 changes: 65 additions & 0 deletions src/Rules/Properties/InvalidCallablePropertyTypeRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Properties;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\ClassPropertyNode;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\CallableType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;
use function sprintf;

/**
* @implements Rule<ClassPropertyNode>
*/
class InvalidCallablePropertyTypeRule implements Rule
{

public function getNodeType(): string
{
return ClassPropertyNode::class;
}

public function processNode(Node $node, Scope $scope): array
{
$classReflection = $node->getClassReflection();
$propertyReflection = $classReflection->getNativeProperty($node->getName());

if (!$propertyReflection->hasNativeType()) {
return [];
}

$nativeType = $propertyReflection->getNativeType();
$callableTypes = [];

TypeTraverser::map($nativeType, static function (Type $type, callable $traverse) use (&$callableTypes): Type {
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}

if ($type instanceof CallableType) {
$callableTypes[] = $type;
}

return $type;
});

if ($callableTypes === []) {
return [];
}

return [
RuleErrorBuilder::message(sprintf(
'Property %s::$%s cannot have callable in its type declaration.',
$classReflection->getDisplayName(),
$node->getName(),
))->identifier('property.callableType')->nonIgnorable()->build(),
];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Properties;

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

/**
* @extends RuleTestCase<InvalidCallablePropertyTypeRule>
*/
class InvalidCallablePropertyTypeRuleTest extends RuleTestCase
{

protected function getRule(): Rule
{
return new InvalidCallablePropertyTypeRule();
}

public function testRule(): void
{
$this->analyse([__DIR__ . '/data/invalid-callable-property-type.php'], [
[
'Property InvalidCallablePropertyType\HelloWorld::$a cannot have callable in its type declaration.',
9,
],
[
'Property InvalidCallablePropertyType\HelloWorld::$b cannot have callable in its type declaration.',
12,
],
[
'Property InvalidCallablePropertyType\HelloWorld::$c cannot have callable in its type declaration.',
15,
],
[
'Property InvalidCallablePropertyType\HelloWorld::$callback cannot have callable in its type declaration.',
23,
],
]);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php declare(strict_types = 1);

namespace InvalidCallablePropertyType;

class HelloWorld
{

/** @var callable(): void */
public callable $a;

/** @var callable(): void|null */
public ?callable $b;

/** @var callable(): void|string */
public callable|string $c;

/**
* @param \Closure(): void $closure
* @param callable(): void $callback
*/
public function __construct(
\Closure $closure,
public callable $callback
)
{
$this->a = $closure;
$this->b = $closure;
$this->c = $closure;
}

}

0 comments on commit 9697760

Please sign in to comment.