1
0
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:
Matt Brown 2017-08-11 18:30:58 -04:00
parent 396d214e3c
commit 17a900ab54
3 changed files with 73 additions and 5 deletions

View File

@ -1759,7 +1759,8 @@ class CallChecker
$fleshed_out_type,
$cased_method_id,
$argument_offset,
new CodeLocation($statements_checker->getSource(), $arg->value)
new CodeLocation($statements_checker->getSource(), $arg->value),
$arg->value
) === false) {
return false;
}
@ -2014,7 +2015,8 @@ class CallChecker
Type\Union $param_type,
$cased_method_id,
$argument_offset,
CodeLocation $code_location
CodeLocation $code_location,
PhpParser\Node\Expr $input_expr = null
) {
if ($param_type->isMixed()) {
return null;
@ -2151,6 +2153,34 @@ class CallChecker
)) {
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;

View File

@ -398,7 +398,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
$this->aliases = $this->file_aliases;
} elseif ($node instanceof PhpParser\Node\Stmt\ClassLike) {
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;
@ -473,7 +473,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
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;
@ -978,7 +978,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
Config $config
) {
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();

View File

@ -103,6 +103,24 @@ class ClosureTest extends TestCase
$take_string = function(string $s) : string { return $s; };
$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_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' => [
'<?php
/**