mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Improve function call resolution
This commit is contained in:
parent
632f613ba0
commit
f5378bdca8
@ -200,10 +200,39 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
|
|||||||
$stmt->inferredType = Type::getMixed();
|
$stmt->inferredType = Type::getMixed();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$function_id = implode('\\', $stmt->name->parts);
|
$original_function_id = implode('\\', $stmt->name->parts);
|
||||||
|
|
||||||
$in_call_map = CallMap::inCallMap($function_id);
|
if (!$stmt->name instanceof PhpParser\Node\Name\FullyQualified) {
|
||||||
$is_stubbed = $codebase_functions->hasStubbedFunction($function_id);
|
$function_id = $codebase_functions->getFullyQualifiedFunctionNameFromString(
|
||||||
|
$original_function_id,
|
||||||
|
$statements_analyzer
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$function_id = $original_function_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$namespaced_function_exists = $codebase_functions->functionExists(
|
||||||
|
$statements_analyzer,
|
||||||
|
strtolower($function_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$namespaced_function_exists
|
||||||
|
&& !$stmt->name instanceof PhpParser\Node\Name\FullyQualified
|
||||||
|
) {
|
||||||
|
$in_call_map = CallMap::inCallMap($original_function_id);
|
||||||
|
$is_stubbed = $codebase_functions->hasStubbedFunction($original_function_id);
|
||||||
|
|
||||||
|
if ($is_stubbed || $in_call_map) {
|
||||||
|
$function_id = $original_function_id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$in_call_map = CallMap::inCallMap($function_id);
|
||||||
|
$is_stubbed = $codebase_functions->hasStubbedFunction($function_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_stubbed || $in_call_map || $namespaced_function_exists) {
|
||||||
|
$function_exists = true;
|
||||||
|
}
|
||||||
|
|
||||||
$is_predefined = true;
|
$is_predefined = true;
|
||||||
|
|
||||||
@ -212,17 +241,9 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
|
|||||||
|
|
||||||
if (!$in_call_map) {
|
if (!$in_call_map) {
|
||||||
$predefined_functions = $config->getPredefinedFunctions();
|
$predefined_functions = $config->getPredefinedFunctions();
|
||||||
$is_predefined = isset($predefined_functions[$function_id]);
|
$is_predefined = isset($predefined_functions[strtolower($original_function_id)])
|
||||||
}
|
|| isset($predefined_functions[strtolower($function_id)]);
|
||||||
|
|
||||||
if (!$in_call_map && !$stmt->name instanceof PhpParser\Node\Name\FullyQualified) {
|
|
||||||
$function_id = $codebase_functions->getFullyQualifiedFunctionNameFromString(
|
|
||||||
$function_id,
|
|
||||||
$statements_analyzer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$in_call_map) {
|
|
||||||
if ($context->check_functions) {
|
if ($context->check_functions) {
|
||||||
if (self::checkFunctionExists(
|
if (self::checkFunctionExists(
|
||||||
$statements_analyzer,
|
$statements_analyzer,
|
||||||
@ -233,18 +254,7 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
|
|||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$function_id = self::getExistingFunctionId(
|
|
||||||
$statements_analyzer,
|
|
||||||
$function_id,
|
|
||||||
$is_maybe_root_function
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$function_exists = $is_stubbed || $codebase_functions->functionExists(
|
|
||||||
$statements_analyzer,
|
|
||||||
strtolower($function_id)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$function_exists = true;
|
$function_exists = true;
|
||||||
}
|
}
|
||||||
|
@ -2099,41 +2099,6 @@ class CallAnalyzer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param StatementsAnalyzer $statements_analyzer
|
|
||||||
* @param string $function_id
|
|
||||||
* @param bool $can_be_in_root_scope if true, the function can be shortened to the root version
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected static function getExistingFunctionId(
|
|
||||||
StatementsAnalyzer $statements_analyzer,
|
|
||||||
$function_id,
|
|
||||||
$can_be_in_root_scope
|
|
||||||
) {
|
|
||||||
$function_id = strtolower($function_id);
|
|
||||||
|
|
||||||
$codebase = $statements_analyzer->getCodebase();
|
|
||||||
|
|
||||||
if ($codebase->functions->functionExists($statements_analyzer, $function_id)) {
|
|
||||||
return $function_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$can_be_in_root_scope) {
|
|
||||||
return $function_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$root_function_id = preg_replace('/.*\\\/', '', $function_id);
|
|
||||||
|
|
||||||
if ($function_id !== $root_function_id
|
|
||||||
&& $codebase->functions->functionExists($statements_analyzer, $root_function_id)
|
|
||||||
) {
|
|
||||||
return $root_function_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $function_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param PhpParser\Node\Identifier|PhpParser\Node\Name $expr
|
* @param PhpParser\Node\Identifier|PhpParser\Node\Name $expr
|
||||||
* @param \Psalm\Storage\Assertion[] $assertions
|
* @param \Psalm\Storage\Assertion[] $assertions
|
||||||
@ -2159,6 +2124,8 @@ class CallAnalyzer
|
|||||||
foreach ($assertions as $assertion) {
|
foreach ($assertions as $assertion) {
|
||||||
$assertion_var_id = null;
|
$assertion_var_id = null;
|
||||||
|
|
||||||
|
$arg_value = null;
|
||||||
|
|
||||||
if (is_int($assertion->var_id)) {
|
if (is_int($assertion->var_id)) {
|
||||||
if (!isset($args[$assertion->var_id])) {
|
if (!isset($args[$assertion->var_id])) {
|
||||||
continue;
|
continue;
|
||||||
@ -2229,6 +2196,23 @@ class CallAnalyzer
|
|||||||
} else {
|
} else {
|
||||||
$type_assertions[$assertion_var_id] = $assertion->rule;
|
$type_assertions[$assertion_var_id] = $assertion->rule;
|
||||||
}
|
}
|
||||||
|
} elseif ($arg_value && $assertion->rule === [['!falsy']]) {
|
||||||
|
$assert_clauses = \Psalm\Type\Algebra::getFormula(
|
||||||
|
$arg_value,
|
||||||
|
$statements_analyzer->getFQCLN(),
|
||||||
|
$statements_analyzer,
|
||||||
|
$statements_analyzer->getCodebase()
|
||||||
|
);
|
||||||
|
|
||||||
|
$simplified_clauses = \Psalm\Type\Algebra::simplifyCNF(
|
||||||
|
array_merge($context->clauses, $assert_clauses)
|
||||||
|
);
|
||||||
|
|
||||||
|
$assert_type_assertions = \Psalm\Type\Algebra::getTruthsFromFormula(
|
||||||
|
$simplified_clauses
|
||||||
|
);
|
||||||
|
|
||||||
|
$type_assertions = array_merge($type_assertions, $assert_type_assertions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,23 @@ class AssertTest extends TestCase
|
|||||||
$a->foo();
|
$a->foo();
|
||||||
}',
|
}',
|
||||||
],
|
],
|
||||||
|
'dropInReplacementForAssert' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @param mixed $_b
|
||||||
|
* @psalm-assert !falsy $_b
|
||||||
|
*/
|
||||||
|
function myAssert($_b) : void {
|
||||||
|
if (!$_b) {
|
||||||
|
throw new \Exception("bad");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bar(?string $s) : string {
|
||||||
|
myAssert($s !== null);
|
||||||
|
return $s;
|
||||||
|
}'
|
||||||
|
],
|
||||||
'assertInstanceOfInterface' => [
|
'assertInstanceOfInterface' => [
|
||||||
'<?php
|
'<?php
|
||||||
class A {
|
class A {
|
||||||
|
@ -1268,6 +1268,22 @@ class FunctionCallTest extends TestCase
|
|||||||
foo($a);
|
foo($a);
|
||||||
}',
|
}',
|
||||||
],
|
],
|
||||||
|
'SKIPPED-getTypeHasValues' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @param mixed $maybe
|
||||||
|
*/
|
||||||
|
function matchesTypes($maybe) : void {
|
||||||
|
$t = gettype($maybe);
|
||||||
|
if ($t === "object") {}
|
||||||
|
}'
|
||||||
|
],
|
||||||
|
'functionResolutionInNamespace' => [
|
||||||
|
'<?php
|
||||||
|
namespace Foo;
|
||||||
|
function sort(int $_) : void {}
|
||||||
|
sort(5);'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user