mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +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();
|
||||
}
|
||||
} else {
|
||||
$function_id = implode('\\', $stmt->name->parts);
|
||||
$original_function_id = implode('\\', $stmt->name->parts);
|
||||
|
||||
$in_call_map = CallMap::inCallMap($function_id);
|
||||
$is_stubbed = $codebase_functions->hasStubbedFunction($function_id);
|
||||
if (!$stmt->name instanceof PhpParser\Node\Name\FullyQualified) {
|
||||
$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;
|
||||
|
||||
@ -212,17 +241,9 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
|
||||
|
||||
if (!$in_call_map) {
|
||||
$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 (self::checkFunctionExists(
|
||||
$statements_analyzer,
|
||||
@ -233,18 +254,7 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
|
||||
) {
|
||||
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 {
|
||||
$function_exists = true;
|
||||
}
|
||||
|
@ -2099,41 +2099,6 @@ class CallAnalyzer
|
||||
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 \Psalm\Storage\Assertion[] $assertions
|
||||
@ -2159,6 +2124,8 @@ class CallAnalyzer
|
||||
foreach ($assertions as $assertion) {
|
||||
$assertion_var_id = null;
|
||||
|
||||
$arg_value = null;
|
||||
|
||||
if (is_int($assertion->var_id)) {
|
||||
if (!isset($args[$assertion->var_id])) {
|
||||
continue;
|
||||
@ -2229,6 +2196,23 @@ class CallAnalyzer
|
||||
} else {
|
||||
$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();
|
||||
}',
|
||||
],
|
||||
'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' => [
|
||||
'<?php
|
||||
class A {
|
||||
|
@ -1268,6 +1268,22 @@ class FunctionCallTest extends TestCase
|
||||
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