FindCallbackArgumentReferences(
        Node functionRoot,
        List<Node> keyReferences,
        List<Node> valueReferences,
        boolean useArrayMode) {
      Preconditions.checkState(functionRoot.isFunction());

      String keyString = null, valueString = null;
      Node callbackParams = NodeUtil.getFunctionParameters(functionRoot);
      Node param = callbackParams.getFirstChild();
      if (param != null) {
        Preconditions.checkState(param.isName());
        keyString = param.getString();

        param = param.getNext();
        if (param != null) {
          Preconditions.checkState(param.isName());
          valueString = param.getString();
        }
      }

      this.keyName = keyString;
      this.valueName = valueString;

      // For arrays, the keyString is the index number of the element.
      // We're interested in the value of the element instead
      if (useArrayMode) {
        this.keyReferences = valueReferences;
        this.valueReferences = keyReferences;
      } else {
        this.keyReferences = keyReferences;
        this.valueReferences = valueReferences;
      }

      this.startingScope = null;
    }
 /**
  * @param fnNode The function to inspect.
  * @return Whether the function has parameters, var, or function declarations.
  */
 private static boolean hasLocalNames(Node fnNode) {
   Node block = NodeUtil.getFunctionBody(fnNode);
   return NodeUtil.getFunctionParameters(fnNode).hasChildren()
       || NodeUtil.has(
           block, new NodeUtil.MatchDeclaration(), new NodeUtil.MatchShallowStatement());
 }
  /** @param fnNode A node for a function for which to generate a type annotation */
  private String getFunctionAnnotation(Node fnNode) {
    Preconditions.checkState(fnNode.isFunction());
    StringBuilder sb = new StringBuilder("/**\n");

    JSType type = fnNode.getJSType();

    if (type == null || type.isUnknownType()) {
      return "";
    }

    FunctionType funType = type.toMaybeFunctionType();

    // We need to use the child nodes of the function as the nodes for the
    // parameters of the function type do not have the real parameter names.
    // FUNCTION
    //   NAME
    //   LP
    //     NAME param1
    //     NAME param2
    if (fnNode != null) {
      Node paramNode = NodeUtil.getFunctionParameters(fnNode).getFirstChild();

      // Param types
      for (Node n : funType.getParameters()) {
        // Bail out if the paramNode is not there.
        if (paramNode == null) {
          break;
        }
        sb.append(" * ");
        appendAnnotation(sb, "param", getParameterNodeJSDocType(n));
        sb.append(" ").append(paramNode.getString()).append("\n");
        paramNode = paramNode.getNext();
      }
    }

    // Return type
    JSType retType = funType.getReturnType();
    if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) {
      sb.append(" * ");
      appendAnnotation(sb, "return", retType.toAnnotationString());
      sb.append("\n");
    }

    // Constructor/interface
    if (funType.isConstructor() || funType.isInterface()) {

      FunctionType superConstructor = funType.getSuperClassConstructor();

      if (superConstructor != null) {
        ObjectType superInstance = funType.getSuperClassConstructor().getInstanceType();
        if (!superInstance.toString().equals("Object")) {
          sb.append(" * ");
          appendAnnotation(sb, "extends", superInstance.toAnnotationString());
          sb.append("\n");
        }
      }

      if (funType.isInterface()) {
        for (ObjectType interfaceType : funType.getExtendedInterfaces()) {
          sb.append(" * ");
          appendAnnotation(sb, "extends", interfaceType.toAnnotationString());
          sb.append("\n");
        }
      }

      // Avoid duplicates, add implemented type to a set first
      Set<String> interfaces = Sets.newTreeSet();
      for (ObjectType interfaze : funType.getImplementedInterfaces()) {
        interfaces.add(interfaze.toAnnotationString());
      }
      for (String interfaze : interfaces) {
        sb.append(" * ");
        appendAnnotation(sb, "implements", interfaze);
        sb.append("\n");
      }

      if (funType.isConstructor()) {
        sb.append(" * @constructor\n");
      } else if (funType.isInterface()) {
        sb.append(" * @interface\n");
      }
    }

    if (fnNode != null && fnNode.getBooleanProp(Node.IS_DISPATCHER)) {
      sb.append(" * @javadispatch\n");
    }

    sb.append(" */\n");
    return sb.toString();
  }