Skip to content

Commit

Permalink
Report also first-class-callables
Browse files Browse the repository at this point in the history
  • Loading branch information
janedbal committed Dec 3, 2024
1 parent 7f66396 commit ae88e19
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 2 deletions.
29 changes: 27 additions & 2 deletions src/Rule/ForbidCustomFunctionsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\ArgumentsNormalizer;
use PHPStan\Analyser\Scope;
use PHPStan\Node\FunctionCallableNode;
use PHPStan\Node\MethodCallableNode;
use PHPStan\Node\StaticMethodCallableNode;
use PHPStan\Reflection\ExtendedMethodReflection;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParametersAcceptor;
Expand All @@ -36,7 +39,7 @@
use function sprintf;

/**
* @implements Rule<CallLike>
* @implements Rule<Node>
*/
class ForbidCustomFunctionsRule implements Rule
{
Expand Down Expand Up @@ -89,14 +92,18 @@ public function __construct(array $forbiddenFunctions, ReflectionProvider $refle

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

/**
* @return list<IdentifierRuleError>
*/
public function processNode(Node $node, Scope $scope): array
{
if ($this->isFirstClassCallableNode($node)) {
$node = $node->getOriginalNode(); // @phpstan-ignore shipmonk.variableTypeOverwritten
}

if ($node instanceof FuncCall) {
return $this->validateFunctionCall($node, $scope);
}
Expand Down Expand Up @@ -310,6 +317,10 @@ private function validateCallable(
*/
private function validateCallLikeArguments(Type $caller, string $methodName, CallLike $node, Scope $scope): array
{
if ($node->isFirstClassCallable()) {
return [];
}

$errors = [];

foreach ($caller->getObjectTypeOrClassStringObjectType()->getObjectClassNames() as $className) {
Expand Down Expand Up @@ -360,6 +371,10 @@ private function validateCallableArguments(
*/
private function validateFunctionArguments(string $functionName, FuncCall $node, Scope $scope): array
{
if ($node->isFirstClassCallable()) {
return [];
}

$functionReflection = $this->getFunctionReflection(new Name($functionName), $scope);

if ($functionReflection === null) {
Expand Down Expand Up @@ -414,4 +429,14 @@ private function getNewCaller(New_ $new, Scope $scope): Type
return $scope->getType($new->class);
}

/**
* @phpstan-assert-if-true FunctionCallableNode|MethodCallableNode|StaticMethodCallableNode $node
*/
private function isFirstClassCallableNode(Node $node): bool
{
return $node instanceof FunctionCallableNode
|| $node instanceof MethodCallableNode
|| $node instanceof StaticMethodCallableNode;
}

}
4 changes: 4 additions & 0 deletions tests/Rule/data/ForbidCustomFunctionsRule/code.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function test(
ChildOfClassWithForbiddenAllMethods $forbiddenClassChild,
SomeInterface $interface
) {
sleep(...); // error: Function sleep() is forbidden. Description 0
sleep(0); // error: Function sleep() is forbidden. Description 0
array_map('sleep', $array); // error: Function sleep() is forbidden. Description 0
array_map(array: $array, callback: 'sleep'); // error: Function sleep() is forbidden. Description 0
Expand Down Expand Up @@ -130,6 +131,7 @@ public function test(
$class->allowedMethod();
$class->forbiddenMethod(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4
$class?->forbiddenMethod(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4
$class->forbiddenMethod(...); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4
$class->allowedInterfaceMethod();
$class->forbiddenInterfaceMethod(); // error: Method ForbidCustomFunctionsRule\SomeInterface::forbiddenInterfaceMethod() is forbidden. Description 6
$class->forbiddenMethodOfParent(); // error: Method ForbidCustomFunctionsRule\SomeParent::forbiddenMethodOfParent() is forbidden. Description 8
Expand All @@ -149,8 +151,10 @@ public function test(

SomeClass::forbiddenInterfaceStaticMethod(); // error: Method ForbidCustomFunctionsRule\SomeInterface::forbiddenInterfaceStaticMethod() is forbidden. Description 7
SomeClass::forbiddenStaticMethod(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5
SomeClass::forbiddenStaticMethod(...); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5

forbidden_namespaced_function(); // error: Function ForbidCustomFunctionsRule\forbidden_namespaced_function() is forbidden. Description 1
forbidden_namespaced_function(...); // error: Function ForbidCustomFunctionsRule\forbidden_namespaced_function() is forbidden. Description 1

$forbiddenClassName = 'ForbidCustomFunctionsRule\ClassWithForbiddenConstructor';
$forbiddenMethodName = 'forbiddenMethod';
Expand Down

0 comments on commit ae88e19

Please sign in to comment.