/** Builds the function type, and puts it in the registry. */
  FunctionType buildAndRegister() {
    if (returnType == null) {
      returnType = typeRegistry.getNativeType(UNKNOWN_TYPE);
    }

    if (parametersNode == null) {
      throw new IllegalStateException("All Function types must have params and a return type");
    }

    FunctionType fnType;
    if (isConstructor) {
      fnType = getOrCreateConstructor();
    } else if (isInterface) {
      fnType = typeRegistry.createInterfaceType(fnName, sourceNode);
      if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) {
        typeRegistry.declareType(fnName, fnType.getInstanceType());
      }
      maybeSetBaseType(fnType);
    } else {
      fnType =
          new FunctionBuilder(typeRegistry)
              .withName(fnName)
              .withSourceNode(sourceNode)
              .withParamsNode(parametersNode)
              .withReturnType(returnType, returnTypeInferred)
              .withTypeOfThis(thisType)
              .withTemplateName(templateTypeName)
              .build();
      maybeSetBaseType(fnType);
    }

    if (implementedInterfaces != null) {
      fnType.setImplementedInterfaces(implementedInterfaces);
    }

    typeRegistry.clearTemplateTypeName();

    return fnType;
  }
  /**
   * Returns a constructor function either by returning it from the registry if it exists or
   * creating and registering a new type. If there is already a type, then warn if the existing type
   * is different than the one we are creating, though still return the existing function if
   * possible. The primary purpose of this is that registering a constructor will fail for all
   * built-in types that are initialized in {@link JSTypeRegistry}. We a) want to make sure that the
   * type information specified in the externs file matches what is in the registry and b) annotate
   * the externs with the {@link JSType} from the registry so that there are not two separate JSType
   * objects for one type.
   */
  private FunctionType getOrCreateConstructor() {
    FunctionType fnType =
        typeRegistry.createConstructorType(fnName, sourceNode, parametersNode, returnType);
    JSType existingType = typeRegistry.getType(fnName);

    if (existingType != null) {
      boolean isInstanceObject = existingType instanceof InstanceObjectType;
      if (isInstanceObject || fnName.equals("Function")) {
        FunctionType existingFn =
            isInstanceObject
                ? ((InstanceObjectType) existingType).getConstructor()
                : typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE);

        if (existingFn.getSource() == null) {
          existingFn.setSource(sourceNode);
        }

        if (!existingFn.hasEqualCallType(fnType)) {
          reportWarning(TYPE_REDEFINITION, fnName, fnType.toString(), existingFn.toString());
        }

        return existingFn;
      } else {
        // We fall through and return the created type, even though it will fail
        // to register. We have no choice as we have to return a function. We
        // issue an error elsewhere though, so the user should fix it.
      }
    }

    maybeSetBaseType(fnType);

    if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) {
      typeRegistry.declareType(fnName, fnType.getInstanceType());
    }
    return fnType;
  }