/**
   * Infers the type of {@code this}.
   *
   * @param info The JSDocInfo for this function.
   * @param owner The node for the object whose prototype "owns" this function. For example, {@code
   *     A} in the expression {@code A.prototype.foo}. May be null to indicate that this is not a
   *     prototype property.
   */
  FunctionTypeBuilder inferThisType(JSDocInfo info, @Nullable Node owner) {
    ObjectType maybeThisType = null;
    if (info != null && info.hasThisType()) {
      maybeThisType = ObjectType.cast(info.getThisType().evaluate(scope, typeRegistry));
    }
    if (maybeThisType != null) {
      thisType = maybeThisType;
      thisType.setValidator(new ThisTypeValidator());
    } else if (owner != null && (info == null || !info.hasType())) {
      // If the function is of the form:
      // x.prototype.y = function() {}
      // then we can assume "x" is the @this type. On the other hand,
      // if it's of the form:
      // /** @type {Function} */ x.prototype.y;
      // then we should not give it a @this type.
      String ownerTypeName = owner.getQualifiedName();
      ObjectType ownerType =
          ObjectType.cast(
              typeRegistry.getForgivingType(
                  scope, ownerTypeName, sourceName, owner.getLineno(), owner.getCharno()));
      if (ownerType != null) {
        thisType = ownerType;
      }
    }

    return this;
  }
    @Override
    public void recordInterfaces(
        JSType type, JSType relatedType, DisambiguateProperties<JSType>.Property p) {
      ObjectType objType = ObjectType.cast(type);
      if (objType != null) {
        FunctionType constructor;
        if (objType instanceof FunctionType) {
          constructor = (FunctionType) objType;
        } else if (objType instanceof FunctionPrototypeType) {
          constructor = ((FunctionPrototypeType) objType).getOwnerFunction();
        } else {
          constructor = objType.getConstructor();
        }
        while (constructor != null) {
          for (ObjectType itype : constructor.getImplementedInterfaces()) {
            JSType top = getTypeWithProperty(p.name, itype);
            if (top != null) {
              p.addType(itype, top, relatedType);
            } else {
              recordInterfaces(itype, relatedType, p);
            }

            // If this interface invalidated this property, return now.
            if (p.skipRenaming) return;
          }
          if (constructor.isInterface() || constructor.isConstructor()) {
            constructor = constructor.getSuperClassConstructor();
          } else {
            constructor = null;
          }
        }
      }
    }
 /**
  * Infers the type of {@code this}.
  *
  * @param type The type of this.
  */
 FunctionTypeBuilder inferThisType(JSDocInfo info, JSType type) {
   ObjectType objType = ObjectType.cast(type);
   if (objType != null && (info == null || !info.hasType())) {
     thisType = objType;
   }
   return this;
 }
    @Override
    public boolean isInvalidatingType(JSType type) {
      if (type == null
          || invalidatingTypes.contains(type)
          || type.isUnknownType() /* unresolved types */) {
        return true;
      }

      ObjectType objType = ObjectType.cast(type);
      return objType != null && !objType.hasReferenceName();
    }
    @Override
    public ObjectType getTypeWithProperty(String field, JSType type) {
      if (!(type instanceof ObjectType)) {
        if (type.autoboxesTo() != null) {
          type = type.autoboxesTo();
        } else {
          return null;
        }
      }

      // Ignore the prototype itself at all times.
      if ("prototype".equals(field)) {
        return null;
      }

      // We look up the prototype chain to find the highest place (if any) that
      // this appears.  This will make references to overriden properties look
      // like references to the initial property, so they are renamed alike.
      ObjectType foundType = null;
      ObjectType objType = ObjectType.cast(type);
      while (objType != null && objType.getImplicitPrototype() != objType) {
        if (objType.hasOwnProperty(field)) {
          foundType = objType;
        }
        objType = objType.getImplicitPrototype();
      }
      // If the property does not exist on the referenced type but the original
      // type is an object type, see if any subtype has the property.
      if (foundType == null) {
        ObjectType maybeType =
            ObjectType.cast(registry.getGreatestSubtypeWithProperty(type, field));
        // getGreatestSubtypeWithProperty does not guarantee that the property
        // is defined on the returned type, it just indicates that it might be,
        // so we have to double check.
        if (maybeType != null && maybeType.hasOwnProperty(field)) {
          foundType = maybeType;
        }
      }
      return foundType;
    }
  /** Invalidates the given type, so that no properties on it will be renamed. */
  private void addInvalidatingType(JSType type) {
    type = type.restrictByNotNullOrUndefined();
    if (type instanceof UnionType) {
      for (JSType alt : ((UnionType) type).getAlternates()) {
        addInvalidatingType(alt);
      }
      return;
    }

    typeSystem.addInvalidatingType(type);
    ObjectType objType = ObjectType.cast(type);
    if (objType != null && objType.getImplicitPrototype() != null) {
      typeSystem.addInvalidatingType(objType.getImplicitPrototype());
    }
  }
  /** Gets the type of {@code this} in the current scope. */
  @Override
  public JSType getTypeOfThis() {
    if (isGlobal()) {
      return ObjectType.cast(rootNode.getJSType());
    }

    Preconditions.checkState(rootNode.isFunction());
    JSType nodeType = rootNode.getJSType();
    if (nodeType != null && nodeType.isFunctionType()) {
      return nodeType.toMaybeFunctionType().getTypeOfThis();
    } else {
      // Executed when the current scope has not been typechecked.
      return null;
    }
  }
 @Override
 public boolean apply(JSType type) {
   ObjectType objectType = ObjectType.cast(type);
   if (objectType == null) {
     reportError(BAD_IMPLEMENTED_TYPE, fnName);
   } else if (objectType.isUnknownType()
       &&
       // If this has a supertype that hasn't been resolved yet,
       // then we can assume this type will be ok once the super
       // type resolves.
       (objectType.getImplicitPrototype() == null
           || objectType.getImplicitPrototype().isResolved())) {
     reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName);
   } else {
     return true;
   }
   return false;
 }
 @Override
 public boolean apply(JSType type) {
   ObjectType objectType = ObjectType.cast(type);
   if (objectType == null) {
     reportWarning(EXTENDS_NON_OBJECT, fnName, type.toString());
   } else if (objectType.isUnknownType()
       &&
       // If this has a supertype that hasn't been resolved yet,
       // then we can assume this type will be ok once the super
       // type resolves.
       (objectType.getImplicitPrototype() == null
           || objectType.getImplicitPrototype().isResolved())) {
     reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName);
   } else {
     return true;
   }
   return false;
 }