code: private void traverseFunction(Node n, Node parent) {
      final Node fnName = n.getFirstChild();
      // NOTE: If a function declaration is the root of a traversal, then we will treat it as a
      // function expression (since 'parent' is null, even though 'n' actually has a parent node) and
      // traverse the function name before entering the scope, rather than afterwards. Removing the
      // null check for 'parent' seems safe, but causes a rare crash when traverseScopeRoots is called
      // by PeepholeOptimizationsPass on a function that somehow doesn't actually have a parent at all
      // (presumably because it's already been removed from the AST?) so that doesn't actually work.
      // This does not actually change anything, though, since rooting a traversal at a function node
      // causes the function scope to be entered twice (unless using #traverseAtScope, which doesn't
      // call this method), so that the name node is just always traversed inside the scope anyway.
      boolean isFunctionDeclaration = parent != null && NodeUtil.isFunctionDeclaration(n);
  
      if (isFunctionDeclaration) {
        // Function declarations are in the scope containing the declaration.
        traverseBranch(fnName, n);
      }
  
      curNode = n;
      pushScope(n);
  
      if (!isFunctionDeclaration) {
        // 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();
    }
docs: Traverses a function.