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, $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;

View File

@ -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();

View File

@ -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
/** /**