/**
* For 'closed context' items (classes, methods, functions,
* closures), we analyze children in the parent context, but
* then return the parent context itself unmodified by the
* children.
*
* │
* ▼
* ┌──●────┐
* │ │
* ●──●──● │
* ┌────┘
* ●
* │
* ▼
*
* @param Node $node
* An AST node we'd like to determine the UnionType
* for
*
* @return Context
* The updated context after visiting the node
*/
public function visitClosedContext(Node $node) : Context
{
// Make a copy of the internal context so that we don't
// leak any changes within the closed context to the
// outer scope
$context = clone $this->context->withLineNumberStart($node->lineno ?? 0);
// Visit the given node populating the code base
// with anything we learn and get a new context
// indicating the state of the world within the
// given node
$context = (new PreOrderAnalysisVisitor($this->code_base, $context))($node);
assert(!empty($context), 'Context cannot be null');
// We collect all child context so that the
// PostOrderAnalysisVisitor can optionally operate on
// them
$child_context_list = [];
$child_context = $context;
// With a context that is inside of the node passed
// to this method, we analyze all children of the
// node.
foreach ($node->children ?? [] as $child_node) {
// Skip any non Node children.
if (!$child_node instanceof Node) {
continue;
}
if (!Analysis::shouldVisit($child_node)) {
$child_context->withLineNumberStart($child_node->lineno ?? 0);
continue;
}
// Step into each child node and get an
// updated context for the node
$child_context = (new BlockAnalysisVisitor($this->code_base, $child_context, $node, $this->depth + 1))($child_node);
$child_context_list[] = $child_context;
}
// For if statements, we need to merge the contexts
// of all child context into a single scope based
// on any possible branching structure
$context = (new ContextMergeVisitor($this->code_base, $context, $child_context_list))($node);
// Now that we know all about our context (like what
// 'self' means), we can analyze statements like
// assignments and method calls.
$context = (new PostOrderAnalysisVisitor($this->code_base, $context->withLineNumberStart($node->lineno ?? 0), $this->parent_node))($node);
// Return the initial context as we exit
return $this->context;
}