mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Add ability to detect invalid callable functions
This commit is contained in:
parent
396d214e3c
commit
17a900ab54
@ -1759,7 +1759,8 @@ class CallChecker
|
|||||||
$fleshed_out_type,
|
$fleshed_out_type,
|
||||||
$cased_method_id,
|
$cased_method_id,
|
||||||
$argument_offset,
|
$argument_offset,
|
||||||
new CodeLocation($statements_checker->getSource(), $arg->value)
|
new CodeLocation($statements_checker->getSource(), $arg->value),
|
||||||
|
$arg->value
|
||||||
) === false) {
|
) === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2014,7 +2015,8 @@ class CallChecker
|
|||||||
Type\Union $param_type,
|
Type\Union $param_type,
|
||||||
$cased_method_id,
|
$cased_method_id,
|
||||||
$argument_offset,
|
$argument_offset,
|
||||||
CodeLocation $code_location
|
CodeLocation $code_location,
|
||||||
|
PhpParser\Node\Expr $input_expr = null
|
||||||
) {
|
) {
|
||||||
if ($param_type->isMixed()) {
|
if ($param_type->isMixed()) {
|
||||||
return null;
|
return null;
|
||||||
@ -2151,6 +2153,34 @@ class CallChecker
|
|||||||
)) {
|
)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} elseif ($input_expr instanceof PhpParser\Node\Scalar\String_) {
|
||||||
|
foreach ($param_type->types as $param_type_part) {
|
||||||
|
if ($param_type_part instanceof TCallable) {
|
||||||
|
$function_name = $input_expr->value;
|
||||||
|
|
||||||
|
if (strpos($function_name, '::') !== false) {
|
||||||
|
if (MethodChecker::checkMethodExists(
|
||||||
|
$project_checker,
|
||||||
|
$function_name,
|
||||||
|
$code_location,
|
||||||
|
$statements_checker->getSuppressedIssues()
|
||||||
|
) === false
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (self::checkFunctionExists(
|
||||||
|
$project_checker,
|
||||||
|
$statements_checker,
|
||||||
|
$function_name,
|
||||||
|
$code_location
|
||||||
|
) === false
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -398,7 +398,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
|||||||
$this->aliases = $this->file_aliases;
|
$this->aliases = $this->file_aliases;
|
||||||
} elseif ($node instanceof PhpParser\Node\Stmt\ClassLike) {
|
} elseif ($node instanceof PhpParser\Node\Stmt\ClassLike) {
|
||||||
if (!$this->fq_classlike_name) {
|
if (!$this->fq_classlike_name) {
|
||||||
throw new \LogicException('$this->fq_classlike_name should bot be null');
|
throw new \LogicException('$this->fq_classlike_name should not be null');
|
||||||
}
|
}
|
||||||
|
|
||||||
$fq_classlike_name = $this->fq_classlike_name;
|
$fq_classlike_name = $this->fq_classlike_name;
|
||||||
@ -473,7 +473,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
|||||||
}
|
}
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
|
||||||
if (!$this->fq_classlike_name) {
|
if (!$this->fq_classlike_name) {
|
||||||
throw new \LogicException('$this->fq_classlike_name should bot be null');
|
throw new \LogicException('$this->fq_classlike_name should not be null');
|
||||||
}
|
}
|
||||||
|
|
||||||
$fq_classlike_name = $this->fq_classlike_name;
|
$fq_classlike_name = $this->fq_classlike_name;
|
||||||
@ -978,7 +978,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
|||||||
Config $config
|
Config $config
|
||||||
) {
|
) {
|
||||||
if (!$this->fq_classlike_name) {
|
if (!$this->fq_classlike_name) {
|
||||||
throw new \LogicException('$this->fq_classlike_name should bot be null');
|
throw new \LogicException('$this->fq_classlike_name should not be null');
|
||||||
}
|
}
|
||||||
|
|
||||||
$comment = $stmt->getDocComment();
|
$comment = $stmt->getDocComment();
|
||||||
|
@ -103,6 +103,24 @@ class ClosureTest extends TestCase
|
|||||||
$take_string = function(string $s) : string { return $s; };
|
$take_string = function(string $s) : string { return $s; };
|
||||||
$take_string("string");',
|
$take_string("string");',
|
||||||
],
|
],
|
||||||
|
'callableMethod' => [
|
||||||
|
'<?php
|
||||||
|
class A {
|
||||||
|
public static function bar(string $a) : string {
|
||||||
|
return $a . "b";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function foo(callable $c) : void {}
|
||||||
|
|
||||||
|
foo("A::bar");',
|
||||||
|
],
|
||||||
|
'callableFunction' => [
|
||||||
|
'<?php
|
||||||
|
function foo(callable $c) : void {}
|
||||||
|
|
||||||
|
foo("trim");',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +169,26 @@ class ClosureTest extends TestCase
|
|||||||
'error_message' => 'InvalidFunctionCall',
|
'error_message' => 'InvalidFunctionCall',
|
||||||
'error_levels' => ['UndefinedClass'],
|
'error_levels' => ['UndefinedClass'],
|
||||||
],
|
],
|
||||||
|
'undefinedCallableMethod' => [
|
||||||
|
'<?php
|
||||||
|
class A {
|
||||||
|
public static function bar(string $a) : string {
|
||||||
|
return $a . "b";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function foo(callable $c) : void {}
|
||||||
|
|
||||||
|
foo("A::barr");',
|
||||||
|
'error_message' => 'UndefinedMethod',
|
||||||
|
],
|
||||||
|
'undefinedCallableFunction' => [
|
||||||
|
'<?php
|
||||||
|
function foo(callable $c) : void {}
|
||||||
|
|
||||||
|
foo("trime");',
|
||||||
|
'error_message' => 'UndefinedFunction',
|
||||||
|
],
|
||||||
'possiblyNullFunctionCall' => [
|
'possiblyNullFunctionCall' => [
|
||||||
'<?php
|
'<?php
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user