/** Traverses a parse tree recursively with a scope, starting at that scope's root. */
  void traverseAtScope(Scope s) {
    Node n = s.getRootNode();
    if (n.isFunction()) {
      // We need to do some extra magic to make sure that the scope doesn't
      // get re-created when we dive into the function.
      if (inputId == null) {
        inputId = NodeUtil.getInputId(n);
      }
      sourceName = getSourceName(n);
      curNode = n;
      pushScope(s);

      Node args = n.getFirstChild().getNext();
      Node body = args.getNext();
      traverseBranch(args, n);
      traverseBranch(body, n);

      popScope();
    } else if (n.isBlock()) {
      if (inputId == null) {
        inputId = NodeUtil.getInputId(n);
      }
      sourceName = getSourceName(n);
      curNode = n;
      pushScope(s);
      traverseBranch(n, n.getParent());

      popScope();
    } else {
      Preconditions.checkState(s.isGlobal(), "Expected global scope. Got:", s);
      traverseWithScope(n, s);
    }
  }
 /** Traverses a non-function block. */
 private void traverseBlockScope(Node n) {
   pushScope(n);
   for (Node child : n.children()) {
     traverseBranch(child, n);
   }
   popScope();
 }
  /** Traverses a function. */
  private void traverseFunction(Node n, Node parent) {
    Preconditions.checkState(n.getChildCount() == 3);
    Preconditions.checkState(n.isFunction());

    final Node fnName = n.getFirstChild();
    boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n);

    if (!isFunctionExpression) {
      // Functions declarations are in the scope containing the declaration.
      traverseBranch(fnName, n);
    }

    curNode = n;
    pushScope(n);

    if (isFunctionExpression) {
      // Function expression names are only accessible within the function
      // scope.
      traverseBranch(fnName, n);
    }

    final Node args = fnName.getNext();
    final Node body = args.getNext();

    // Args
    traverseBranch(args, n);

    // Body
    // ES6 "arrow" function may not have a block as a body.
    traverseBranch(body, n);

    popScope();
  }
 /**
  * Traverse a function out-of-band of normal traversal.
  *
  * @param node The function node.
  * @param scope The scope the function is contained in. Does not fire enter/exit callback events
  *     for this scope.
  */
 public void traverseFunctionOutOfBand(Node node, Scope scope) {
   Preconditions.checkNotNull(scope);
   Preconditions.checkState(node.isFunction());
   Preconditions.checkState(scope.getRootNode() != null);
   if (inputId == null) {
     inputId = NodeUtil.getInputId(node);
   }
   curNode = node.getParent();
   pushScope(scope, true /* quietly */);
   traverseBranch(node, curNode);
   popScope(true /* quietly */);
 }
 /**
  * Traverses a parse tree recursively with a scope, starting with the given root. This should only
  * be used in the global scope. Otherwise, use {@link #traverseAtScope}.
  */
 void traverseWithScope(Node root, Scope s) {
   Preconditions.checkState(s.isGlobal());
   try {
     inputId = null;
     sourceName = "";
     curNode = root;
     pushScope(s);
     traverseBranch(root, null);
     popScope();
   } catch (Exception unexpectedException) {
     throwUnexpectedException(unexpectedException);
   }
 }
 /** Traverses a parse tree recursively. */
 public void traverse(Node root) {
   try {
     inputId = NodeUtil.getInputId(root);
     sourceName = "";
     curNode = root;
     pushScope(root);
     // null parent ensures that the shallow callbacks will traverse root
     traverseBranch(root, null);
     popScope();
   } catch (Exception unexpectedException) {
     throwUnexpectedException(unexpectedException);
   }
 }
 /**
  * Traverses an inner node recursively with a refined scope. An inner node may be any node with a
  * non {@code null} parent (i.e. all nodes except the root).
  *
  * @param node the node to traverse
  * @param parent the node's parent, it may not be {@code null}
  * @param refinedScope the refined scope of the scope currently at the top of the scope stack or
  *     in trivial cases that very scope or {@code null}
  */
 protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) {
   Preconditions.checkNotNull(parent);
   if (inputId == null) {
     inputId = NodeUtil.getInputId(node);
   }
   if (refinedScope != null && getScope() != refinedScope) {
     curNode = node;
     pushScope(refinedScope);
     traverseBranch(node, parent);
     popScope();
   } else {
     traverseBranch(node, parent);
   }
 }
  void traverseRoots(Node externs, Node root) {
    try {
      Node scopeRoot = externs.getParent();
      Preconditions.checkState(scopeRoot != null);

      inputId = NodeUtil.getInputId(scopeRoot);
      sourceName = "";
      curNode = scopeRoot;
      pushScope(scopeRoot);

      traverseBranch(externs, scopeRoot);
      Preconditions.checkState(root.getParent() == scopeRoot);
      traverseBranch(root, scopeRoot);

      popScope();
    } catch (Exception unexpectedException) {
      throwUnexpectedException(unexpectedException);
    }
  }
 /** Creates a new scope (e.g. when entering a function). */
 private void pushScope(Scope s) {
   pushScope(s, false);
 }