/** 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());
    }
  }
 @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;
 }
    @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;
    }
    private Set<JSType> getTypesToSkipForTypeNonUnion(JSType type) {
      Set<JSType> types = Sets.newHashSet();
      JSType skipType = type;
      while (skipType != null) {
        types.add(skipType);

        ObjectType objSkipType = skipType.toObjectType();
        if (objSkipType != null) {
          skipType = objSkipType.getImplicitPrototype();
        } else {
          break;
        }
      }
      return types;
    }