fun overrides()

in compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XOverrides.kt [36:140]


  fun overrides(
      overrider: XMethodElement,
      overridden: XMethodElement,
      inType: XTypeElement,
      env: XProcessingEnv,
      useMoreElements: Boolean = true,
  ): Boolean {
    if (useMoreElements) {
      return when (env.backend) {
        XProcessingEnv.Backend.JAVAC -> {
          MoreElements.overrides(
              overrider.toJavac(),
              overridden.toJavac(),
              inType.toJavac(),
              env.toJavac().typeUtils,
          )
          false
        }
        XProcessingEnv.Backend.KSP -> {
          env.resolver()?.overrides(overrider.toKS(), overridden.toKS(), inType.toKS()) ?: false
        }
      }
    }

    if (overrider.name != overridden.name) {
      // They must have the same name.
      return false
    }
    // We should just be able to write overrider.equals(overridden) here, but that runs afoul
    // of a problem with Eclipse. If for example you look at the method Stream<E> stream() in
    // Collection<E>, as obtained by collectionTypeElement.getEnclosedElements(), it will not
    // compare equal to the method Stream<E> stream() as obtained by
    // elementUtils.getAllMembers(listTypeElement), even though List<E> inherits the method
    // from Collection<E>. The reason is that, in ecj, getAllMembers does type substitution,
    // so the return type of stream() is Stream<E'>, where E' is the E from List<E> rather than
    // the one from Collection<E>. Instead we compare the enclosing element, which will be
    // Collection<E> no matter how we got the method. If two methods are in the same type
    // then it's impossible for one to override the other, regardless of whether they are the
    // same method.
    if (overrider.enclosingElement == overridden.enclosingElement) {
      return false
    }
    if (overridden.isStatic()) {
      // Static methods can't be overridden (though they can be hidden by other static methods).
      return false
    }
    val overriddenVisibility: Visibility = Visibility.of(overridden)
    val overriderVisibility: Visibility = Visibility.of(overrider)
    if (overridden.isPrivate() || overriderVisibility.compareTo(overriddenVisibility) < 0) {
      // Private methods can't be overridden, and methods can't be overridden by less-visible
      // methods. The latter condition is enforced by the compiler so in theory we might report
      // an "incorrect" result here for code that javac would not have allowed.
      return false
    }
    // TODO: do this right (added by me)
    if (overrider.parameters.size != overridden.parameters.size) {
      return false
    }
    for (i in overrider.parameters.indices) {
      if (!overrider.parameters[i].type.isSameType(overridden.parameters[i].type)) {
        return false
      }
    }
    val overriddenType = overridden.enclosingElement as? XTypeElement
    return if (inType.isClass()) {
      // Method mC in or inherited by class C (JLS 8.4.8.1)...
      if (overriddenType?.isClass() == true) {
        // ...overrides from C another method mA declared in class A. The only condition we
        // haven't checked is that C does not inherit mA. Ideally we could just write this:
        //    return !elementUtils.getAllMembers(in).contains(overridden);
        // But that doesn't work in Eclipse. For example, getAllMembers(AbstractList)
        // contains List.isEmpty() where you might reasonably expect it to contain
        // AbstractCollection.isEmpty(). So we need to visit superclasses until we reach
        // one that declares the same method, and check that we haven't reached mA. We compare
        // the enclosing elements rather than the methods themselves for the reason described
        // at the start of the method.
        false
      } else if (overriddenType?.isInterface() == true) {
        // ...overrides from C another method mI declared in interface I. We've already checked
        // the conditions (assuming that the only alternative to mI being abstract or default is
        // mI being static, which we eliminated above). However, it appears that the logic here
        // is necessary in order to be compatible with javac's `overrides` method. An inherited
        // abstract method does not override another method. (But, if it is not inherited,
        // it does, including if inType inherits a concrete method of the same name from its
        // superclass.) Here again we can use getAllMembers with javac but not with ecj. javac
        // says that getAllMembers(AbstractList) contains both AbstractCollection.size() and
        // List.size(), but ecj doesn't have the latter. The spec is not particularly clear so
        // either seems justifiable. So we need to look up the interface path that goes from inType
        // to `overriddenType` (or the several paths if there are several) and apply similar logic
        // to methodFromSuperclasses above.
        if (overrider.isAbstract()) {
          false
        } else {
          true
        }
      } else {
        // We don't know what this is so say no.
        false
      }
    } else {
      overriddenType?.isInterface() == true
      // Method mI in or inherited by interface I (JLS 9.4.1.1). We've already checked everything.
      // If this is not an interface then we don't know what it is so we say no.
    }
  }