/**
* Visit a node with kind `\ast\AST_CALL`
*
* @param Node $node
* A node of the type indicated by the method name that we'd
* like to figure out the type that it produces.
*
* @return UnionType
* The set of types that are possibly produced by the
* given node
*/
public function visitCall(Node $node) : UnionType
{
if ($node->children['expr']->kind !== \ast\AST_NAME) {
// Things like `$func()`
return new UnionType();
}
$function_name = $node->children['expr']->children['name'];
$function_fqsen = null;
// If its not fully qualified
if ($node->children['expr']->flags & \ast\flags\NAME_NOT_FQ) {
// Check to see if we have a mapped name
if ($this->context->hasNamespaceMapFor(T_FUNCTION, $function_name)) {
$function_fqsen = $this->context->getNamespaceMapFor(T_FUNCTION, $function_name);
} else {
$function_fqsen = FullyQualifiedFunctionName::fromStringInContext($function_name, $this->context);
}
// If the name is fully qualified
} else {
$function_fqsen = FullyQualifiedFunctionName::fromFullyQualifiedString($function_name);
}
// If the function doesn't exist, check to see if its
// a call to a builtin method
if (!$this->code_base->hasMethod($function_fqsen)) {
$function_fqsen = FullyQualifiedFunctionName::make('', $function_name);
}
if (!$this->code_base->hasMethod($function_fqsen)) {
// Missing internal (bulitin) method.
return new UnionType();
}
$function = $this->code_base->getMethod($function_fqsen);
// If this is an internal function, see if we can get
// its types from the static dataset.
if ($function->getContext()->isInternal() && $function->getUnionType()->isEmpty()) {
$map = UnionType::internalFunctionSignatureMapForFQSEN($function_fqsen);
return $map[$function_name] ?? new UnionType();
}
return $function->getUnionType();
}