public DeclaredFunctionType withTypeInfoFromSuper(DeclaredFunctionType superType) {
   FunctionTypeBuilder builder = new FunctionTypeBuilder();
   int i = 0;
   for (JSType formal : requiredFormals) {
     builder.addReqFormal(formal != null ? formal : superType.getFormalType(i));
     i++;
   }
   for (JSType formal : optionalFormals) {
     builder.addOptFormal(formal != null ? formal : superType.getFormalType(i));
     i++;
   }
   if (restFormals != null) {
     builder.addRestFormals(restFormals);
   } else if (superType.hasRestFormals()) {
     builder.addRestFormals(superType.restFormals);
   }
   builder.addRetType(returnType != null ? returnType : superType.returnType);
   builder.addNominalType(nominalType);
   builder.addReceiverType(receiverType);
   builder.addTypeParameters(typeParameters);
   return builder.buildDeclaration();
 }
  private static DeclaredFunctionType meet(DeclaredFunctionType f1, DeclaredFunctionType f2) {
    if (f1.equals(f2)) {
      return f1;
    }

    FunctionTypeBuilder builder = new FunctionTypeBuilder();
    int minRequiredArity = Math.min(f1.requiredFormals.size(), f2.requiredFormals.size());
    for (int i = 0; i < minRequiredArity; i++) {
      builder.addReqFormal(nullAcceptingJoin(f1.getFormalType(i), f2.getFormalType(i)));
    }
    int maxTotalArity =
        Math.max(
            f1.requiredFormals.size() + f1.optionalFormals.size(),
            f2.requiredFormals.size() + f2.optionalFormals.size());
    for (int i = minRequiredArity; i < maxTotalArity; i++) {
      builder.addOptFormal(nullAcceptingJoin(f1.getFormalType(i), f2.getFormalType(i)));
    }
    if (f1.restFormals != null || f2.restFormals != null) {
      builder.addRestFormals(nullAcceptingJoin(f1.restFormals, f2.restFormals));
    }
    builder.addRetType(nullAcceptingMeet(f1.returnType, f2.returnType));
    return builder.buildDeclaration();
  }
  private DeclaredFunctionType getFunTypeFromTypicalFunctionJsdoc(
      JSDocInfo jsdoc,
      String functionName,
      Node funNode,
      RawNominalType constructorType,
      RawNominalType ownerType,
      DeclaredTypeRegistry registry,
      FunctionTypeBuilder builder) {
    ImmutableList.Builder<String> typeParamsBuilder = ImmutableList.builder();
    ImmutableList<String> typeParameters = ImmutableList.of();
    Node parent = funNode.getParent();

    // TODO(dimvar): need more @template warnings
    // - warn for multiple @template annotations
    // - warn for @template annotation w/out usage

    boolean ignoreJsdoc = false;
    if (jsdoc != null) {
      if (constructorType != null) {
        // We have created new names for these type variables in GTI, don't
        // create new ones here.
        typeParamsBuilder.addAll(constructorType.getTypeParameters());
      } else {
        for (String typeParam : jsdoc.getTemplateTypeNames()) {
          typeParamsBuilder.add(this.nameGen.getNextName(typeParam));
        }
      }
      // We don't properly support the type transformation language; we treat
      // its type variables as ordinary type variables.
      for (String typeParam : jsdoc.getTypeTransformations().keySet()) {
        typeParamsBuilder.add(this.nameGen.getNextName(typeParam));
      }
      typeParameters = typeParamsBuilder.build();
      if (!typeParameters.isEmpty()) {
        if (parent.isSetterDef() || parent.isGetterDef()) {
          ignoreJsdoc = true;
          jsdoc = null;
          warnings.add(JSError.make(funNode, TEMPLATED_GETTER_SETTER));
        } else {
          builder.addTypeParameters(typeParameters);
        }
      }
    }
    if (ownerType != null) {
      typeParamsBuilder.addAll(ownerType.getTypeParameters());
      typeParameters = typeParamsBuilder.build();
    }

    fillInFormalParameterTypes(jsdoc, funNode, typeParameters, registry, builder, ignoreJsdoc);
    fillInReturnType(jsdoc, funNode, parent, typeParameters, registry, builder, ignoreJsdoc);
    if (jsdoc == null) {
      return builder.buildDeclaration();
    }

    // Look at other annotations, eg, @constructor
    NominalType parentClass =
        getMaybeParentClass(jsdoc, functionName, funNode, typeParameters, registry);
    ImmutableSet<NominalType> implementedIntfs =
        getImplementedInterfaces(jsdoc, registry, typeParameters);
    if (constructorType == null && jsdoc.isConstructorOrInterface()) {
      // Anonymous type, don't register it.
      return builder.buildDeclaration();
    } else if (jsdoc.isConstructor()) {
      handleConstructorAnnotation(
          functionName, funNode, constructorType, parentClass, implementedIntfs, registry, builder);
    } else if (jsdoc.isInterface()) {
      handleInterfaceAnnotation(
          jsdoc,
          functionName,
          funNode,
          constructorType,
          implementedIntfs,
          typeParameters,
          registry,
          builder);
    } else if (!implementedIntfs.isEmpty()) {
      warnings.add(JSError.make(funNode, IMPLEMENTS_WITHOUT_CONSTRUCTOR, functionName));
    }

    if (jsdoc.hasThisType()) {
      Node thisRoot = jsdoc.getThisType().getRoot();
      Preconditions.checkState(thisRoot.getType() == Token.BANG);
      builder.addReceiverType(getThisOrNewType(thisRoot.getFirstChild(), registry, typeParameters));
    }

    return builder.buildDeclaration();
  }