1
0
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:
Matthew Brown 2019-01-05 14:50:11 -05:00
parent 632f613ba0
commit f5378bdca8
4 changed files with 86 additions and 59 deletions

View File

@ -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;
}

View File

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

View File

@ -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 {

View File

@ -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);'
],
];
}