diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php index 508f504e6..367df0290 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php @@ -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; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index cbdfc92f4..42399957e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -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); } } diff --git a/tests/AssertTest.php b/tests/AssertTest.php index 5a8baa5ad..cc01e80dc 100644 --- a/tests/AssertTest.php +++ b/tests/AssertTest.php @@ -30,6 +30,23 @@ class AssertTest extends TestCase $a->foo(); }', ], + 'dropInReplacementForAssert' => [ + ' [ ' [ + ' [ + '