/** 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);
    }
  }
  private void scanRoot(Node n) {
    if (n.isFunction()) {
      if (inputId == null) {
        inputId = NodeUtil.getInputId(n);
        // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached
        // from the AST.
        // Is it meaningful to build a scope for detached FUNCTION node?
      }

      final Node fnNameNode = n.getFirstChild();
      final Node args = fnNameNode.getNext();
      final Node body = args.getNext();

      // Bleed the function name into the scope, if it hasn't
      // been declared in the outer scope.
      String fnName = fnNameNode.getString();
      if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) {
        declareVar(fnNameNode);
      }

      // Args: Declare function variables
      Preconditions.checkState(args.isParamList());
      for (Node a = args.getFirstChild(); a != null; a = a.getNext()) {
        Preconditions.checkState(a.isName());
        declareVar(a);
      }

      // Body
      scanVars(body);
    } else {
      // It's the global block
      Preconditions.checkState(scope.getParent() == null);
      scanVars(n);
    }
  }
 /**
  * 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. */
 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);
    }
  }