/** * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitCall(Node $node) : Context { $expression = $node->children['expr']; if (Config::get()->backward_compatibility_checks) { AST::backwardCompatibilityCheck($this->context, $node); foreach ($node->children['args']->children as $arg_node) { if ($arg_node instanceof Node) { AST::backwardCompatibilityCheck($this->context, $arg_node); } } } if ($expression->kind == \ast\AST_NAME) { try { $method = AST::functionFromNameInContext($expression->children['name'], $this->context, $this->code_base); } catch (CodeBaseException $exception) { Log::err(Log::EUNDEF, $exception->getMessage(), $this->context->getFile(), $node->lineno); return $this->context; } // Check the call for paraemter and argument types $this->analyzeCallToMethod($this->code_base, $method, $node); } else { if ($expression->kind == \ast\AST_VAR) { $variable_name = AST::variableName($expression); if (empty($variable_name)) { return $this->context; } // $var() - hopefully a closure, otherwise we don't know if ($this->context->getScope()->hasVariableWithName($variable_name)) { $variable = $this->context->getScope()->getVariableWithName($variable_name); $union_type = $variable->getUnionType(); if ($union_type->isEmpty()) { return $this->context; } $type = $union_type->head(); if (!$type instanceof CallableType) { return $this->context; } $closure_fqsen = FullyQualifiedFunctionName::fromFullyQualifiedString((string) $type->asFQSEN()); if ($this->code_base->hasMethod($closure_fqsen)) { // Get the closure $method = $this->code_base->getMethod($closure_fqsen); // Check the call for paraemter and argument types $this->analyzeCallToMethod($this->code_base, $method, $node); } } } } return $this->context; }
/** * 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']; try { $function = AST::functionFromNameInContext($function_name, $this->context, $this->code_base); } catch (CodeBaseException $exception) { // If the function wasn't declared, it'll be caught // and reported elsewhere return new UnionType(); } $function_fqsen = $function->getFQSEN(); // TODO: I don't believe we need this any more // 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->getFQSEN()); return $map[$function_name] ?? new UnionType(); } return $function->getUnionType(); }
/** * Visit a node with kind `\ast\AST_FUNC_DECL` * * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitFuncDecl(Node $node) : Context { $function_name = $node->name; try { $method = AST::functionFromNameInContext($function_name, $this->context, $this->code_base, true); } catch (CodeBaseException $exception) { Log::err(Log::EFATAL, $exception->getMessage(), $this->context->getFile(), $node->lineno); return $this->context; } // Hunt for the alternate associated with the file we're // looking at currently in this context. foreach ($method->alternateGenerator($this->code_base) as $i => $alternate_method) { if ($alternate_method->getContext()->getFile() === $this->context->getFile()) { return $method->getContext()->withMethodFQSEN($alternate_method->getFQSEN()); } } // No alternate was found Log::err(Log::EFATAL, "Can't find function {$function_name} - aborting", $this->context->getFile(), $node->lineno); return $this->context; }
/** * 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 = AST::functionFromNameInContext($function_name, $this->context, $this->code_base); $function_fqsen = $function->getFQSEN(); // 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->getFQSEN()); return $map[$function_name] ?? new UnionType(); } return $function->getUnionType(); }