Example #1
0
  @NotNull
  public static <H> H selectMostSpecificMember(
      @NotNull Collection<H> overridables,
      @NotNull Function1<H, CallableDescriptor> descriptorByHandle) {
    assert !overridables.isEmpty() : "Should have at least one overridable descriptor";

    if (overridables.size() == 1) {
      return CollectionsKt.first(overridables);
    }

    Collection<H> candidates = new ArrayList<H>(2);
    List<CallableDescriptor> callableMemberDescriptors =
        CollectionsKt.map(overridables, descriptorByHandle);

    H transitivelyMostSpecific = CollectionsKt.first(overridables);
    CallableDescriptor transitivelyMostSpecificDescriptor =
        descriptorByHandle.invoke(transitivelyMostSpecific);

    for (H overridable : overridables) {
      CallableDescriptor descriptor = descriptorByHandle.invoke(overridable);
      if (isMoreSpecificThenAllOf(descriptor, callableMemberDescriptors)) {
        candidates.add(overridable);
      }
      if (isMoreSpecific(descriptor, transitivelyMostSpecificDescriptor)
          && !isMoreSpecific(transitivelyMostSpecificDescriptor, descriptor)) {
        transitivelyMostSpecific = overridable;
      }
    }

    if (candidates.isEmpty()) {
      return transitivelyMostSpecific;
    } else if (candidates.size() == 1) {
      return CollectionsKt.first(candidates);
    }

    H firstNonFlexible = null;
    for (H candidate : candidates) {
      //noinspection ConstantConditions
      if (!FlexibleTypesKt.isFlexible(descriptorByHandle.invoke(candidate).getReturnType())) {
        firstNonFlexible = candidate;
        break;
      }
    }
    if (firstNonFlexible != null) {
      return firstNonFlexible;
    }

    return CollectionsKt.first(candidates);
  }
Example #2
0
  @NotNull
  @ReadOnly
  public List<ClassDescriptor> getDescriptorsForExtraCompanionObjects() {
    final KtObjectDeclaration allowedCompanionObject = getCompanionObjectIfAllowed();

    return CollectionsKt.map(
        CollectionsKt.filter(
            declarationProvider.getOwnerInfo().getCompanionObjects(),
            new Function1<KtObjectDeclaration, Boolean>() {
              @Override
              public Boolean invoke(KtObjectDeclaration companionObject) {
                return companionObject != allowedCompanionObject;
              }
            }),
        new Function1<KtObjectDeclaration, ClassDescriptor>() {
          @Override
          public ClassDescriptor invoke(KtObjectDeclaration companionObject) {
            return extraCompanionObjectDescriptors.invoke(companionObject);
          }
        });
  }
Example #3
0
 public static boolean dependsOnTypeParameters(
     @NotNull KotlinType type, @NotNull Collection<TypeParameterDescriptor> typeParameters) {
   return dependsOnTypeConstructors(
       type,
       CollectionsKt.map(
           typeParameters,
           new Function1<TypeParameterDescriptor, TypeConstructor>() {
             @Override
             public TypeConstructor invoke(
                 @NotNull TypeParameterDescriptor typeParameterDescriptor) {
               return typeParameterDescriptor.getTypeConstructor();
             }
           }));
 }
Example #4
0
 @NotNull
 private static Collection<CallableMemberDescriptor> filterVisibleFakeOverrides(
     @NotNull final ClassDescriptor current,
     @NotNull Collection<CallableMemberDescriptor> toFilter) {
   return CollectionsKt.filter(
       toFilter,
       new Function1<CallableMemberDescriptor, Boolean>() {
         @Override
         public Boolean invoke(CallableMemberDescriptor descriptor) {
           // nested class could capture private member, so check for private visibility added
           return !Visibilities.isPrivate(descriptor.getVisibility())
               && Visibilities.isVisibleIgnoringReceiver(descriptor, current);
         }
       });
 }
Example #5
0
  private static boolean allHasSameContainingDeclaration(
      @NotNull Collection<CallableMemberDescriptor> notOverridden) {
    if (notOverridden.size() < 2) return true;

    final DeclarationDescriptor containingDeclaration =
        notOverridden.iterator().next().getContainingDeclaration();
    return CollectionsKt.all(
        notOverridden,
        new Function1<CallableMemberDescriptor, Boolean>() {
          @Override
          public Boolean invoke(CallableMemberDescriptor descriptor) {
            return descriptor.getContainingDeclaration() == containingDeclaration;
          }
        });
  }
Example #6
0
  @NotNull
  public static KotlinType substituteParameters(
      @NotNull ClassDescriptor clazz, @NotNull List<KotlinType> typeArguments) {
    List<TypeProjection> projections =
        CollectionsKt.map(
            typeArguments,
            new Function1<KotlinType, TypeProjection>() {
              @Override
              public TypeProjection invoke(KotlinType type) {
                return new TypeProjectionImpl(type);
              }
            });

    return substituteProjectionsForParameters(clazz, projections);
  }
Example #7
0
 @NotNull
 @Override
 public Collection<CallableMemberDescriptor> getDeclaredCallableMembers() {
   //noinspection unchecked
   return (Collection)
       CollectionsKt.filter(
           DescriptorUtils.getAllDescriptors(unsubstitutedMemberScope),
           new Function1<DeclarationDescriptor, Boolean>() {
             @Override
             public Boolean invoke(DeclarationDescriptor descriptor) {
               return descriptor instanceof CallableMemberDescriptor
                   && ((CallableMemberDescriptor) descriptor).getKind()
                       != CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
             }
           });
 }
Example #8
0
public class OverridingUtil {

  private static final List<ExternalOverridabilityCondition> EXTERNAL_CONDITIONS =
      CollectionsKt.toList(
          ServiceLoader.load(
              ExternalOverridabilityCondition.class,
              ExternalOverridabilityCondition.class.getClassLoader()));

  public static final OverridingUtil DEFAULT =
      new OverridingUtil(
          new KotlinTypeChecker.TypeConstructorEquality() {
            @Override
            public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
              return a.equals(b);
            }
          });

  @NotNull
  public static OverridingUtil createWithEqualityAxioms(
      @NotNull KotlinTypeChecker.TypeConstructorEquality equalityAxioms) {
    return new OverridingUtil(equalityAxioms);
  }

  private final KotlinTypeChecker.TypeConstructorEquality equalityAxioms;

  private OverridingUtil(KotlinTypeChecker.TypeConstructorEquality axioms) {
    equalityAxioms = axioms;
  }

  /**
   * Given a set of descriptors, returns a set containing all the given descriptors except those
   * which _are overridden_ by at least one other descriptor from the original set.
   */
  @NotNull
  public static <D extends CallableDescriptor> Set<D> filterOutOverridden(
      @NotNull Set<D> candidateSet) {
    return filterOverrides(candidateSet, FunctionsKt.<CallableDescriptor>identity());
  }

  @NotNull
  public static <D> Set<D> filterOverrides(
      @NotNull Set<D> candidateSet,
      @NotNull Function1<? super D, ? extends CallableDescriptor> transform) {
    if (candidateSet.size() <= 1) return candidateSet;

    Set<D> result = new LinkedHashSet<D>();
    outerLoop:
    for (D meD : candidateSet) {
      CallableDescriptor me = transform.invoke(meD);
      for (Iterator<D> iterator = result.iterator(); iterator.hasNext(); ) {
        D otherD = iterator.next();
        CallableDescriptor other = transform.invoke(otherD);
        if (overrides(me, other)) {
          iterator.remove();
        } else if (overrides(other, me)) {
          continue outerLoop;
        }
      }
      result.add(meD);
    }

    assert !result.isEmpty() : "All candidates filtered out from " + candidateSet;

    return result;
  }

  /** @return whether f overrides g */
  public static <D extends CallableDescriptor> boolean overrides(@NotNull D f, @NotNull D g) {
    // In a multi-module project different "copies" of the same class may be present in different
    // libraries,
    // that's why we use structural equivalence for members (DescriptorEquivalenceForOverrides).

    // This first check cover the case of duplicate classes in different modules:
    // when B is defined in modules m1 and m2, and C (indirectly) inherits from both versions,
    // we'll be getting sets of members that do not override each other, but are structurally
    // equivalent.
    // As other code relies on no equal descriptors passed here, we guard against f == g, but this
    // may not be necessary
    // Note that this is needed for the usage of this function in the IDE code
    if (!f.equals(g)
        && DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(
            f.getOriginal(), g.getOriginal())) return true;

    CallableDescriptor originalG = g.getOriginal();
    for (D overriddenFunction : DescriptorUtils.getAllOverriddenDescriptors(f)) {
      if (DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(originalG, overriddenFunction))
        return true;
    }
    return false;
  }

  /**
   * @return overridden real descriptors (not fake overrides). Note that most usages of this method
   *     should be followed by calling {@link #filterOutOverridden(Set)}, because some of the
   *     declarations can override the other.
   */
  @NotNull
  public static Set<CallableMemberDescriptor> getOverriddenDeclarations(
      @NotNull CallableMemberDescriptor descriptor) {
    Set<CallableMemberDescriptor> result = new LinkedHashSet<CallableMemberDescriptor>();
    collectOverriddenDeclarations(descriptor, result);
    return result;
  }

  private static void collectOverriddenDeclarations(
      @NotNull CallableMemberDescriptor descriptor, @NotNull Set<CallableMemberDescriptor> result) {
    if (descriptor.getKind().isReal()) {
      result.add(descriptor);
    } else {
      if (descriptor.getOverriddenDescriptors().isEmpty()) {
        throw new IllegalStateException(
            "No overridden descriptors found for (fake override) " + descriptor);
      }
      for (CallableMemberDescriptor overridden : descriptor.getOverriddenDescriptors()) {
        collectOverriddenDeclarations(overridden, result);
      }
    }
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableBy(
      @NotNull CallableDescriptor superDescriptor,
      @NotNull CallableDescriptor subDescriptor,
      @Nullable ClassDescriptor subClassDescriptor) {
    return isOverridableBy(superDescriptor, subDescriptor, subClassDescriptor, false);
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableBy(
      @NotNull CallableDescriptor superDescriptor,
      @NotNull CallableDescriptor subDescriptor,
      @Nullable ClassDescriptor subClassDescriptor,
      boolean checkReturnType) {
    OverrideCompatibilityInfo basicResult =
        isOverridableByWithoutExternalConditions(superDescriptor, subDescriptor, checkReturnType);
    boolean wasSuccess = basicResult.getResult() == OVERRIDABLE;

    for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
      // Do not run CONFLICTS_ONLY while there was no success
      if (externalCondition.getContract()
          == ExternalOverridabilityCondition.Contract.CONFLICTS_ONLY) continue;
      if (wasSuccess
          && externalCondition.getContract()
              == ExternalOverridabilityCondition.Contract.SUCCESS_ONLY) continue;

      ExternalOverridabilityCondition.Result result =
          externalCondition.isOverridable(superDescriptor, subDescriptor, subClassDescriptor);

      switch (result) {
        case OVERRIDABLE:
          wasSuccess = true;
          break;
        case CONFLICT:
          return OverrideCompatibilityInfo.conflict("External condition failed");
        case INCOMPATIBLE:
          return OverrideCompatibilityInfo.incompatible("External condition");
        case UNKNOWN:
          // do nothing
          // go to the next external condition or default override check
      }
    }

    if (!wasSuccess) {
      return basicResult;
    }

    // Search for conflicts from external conditions
    for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
      // Run all conditions that was not run before (i.e. CONFLICTS_ONLY)
      if (externalCondition.getContract()
          != ExternalOverridabilityCondition.Contract.CONFLICTS_ONLY) continue;

      ExternalOverridabilityCondition.Result result =
          externalCondition.isOverridable(superDescriptor, subDescriptor, subClassDescriptor);
      switch (result) {
        case CONFLICT:
          return OverrideCompatibilityInfo.conflict("External condition failed");
        case INCOMPATIBLE:
          return OverrideCompatibilityInfo.incompatible("External condition");
        case OVERRIDABLE:
          throw new IllegalStateException(
              "Contract violation in "
                  + externalCondition.getClass().getName()
                  + " condition. It's not supposed to end with success");
        case UNKNOWN:
          // do nothing
          // go to the next external condition or default override check
      }
    }

    return OverrideCompatibilityInfo.success();
  }

  @NotNull
  public OverrideCompatibilityInfo isOverridableByWithoutExternalConditions(
      @NotNull CallableDescriptor superDescriptor,
      @NotNull CallableDescriptor subDescriptor,
      boolean checkReturnType) {
    OverrideCompatibilityInfo basicOverridability =
        getBasicOverridabilityProblem(superDescriptor, subDescriptor);
    if (basicOverridability != null) return basicOverridability;

    List<KotlinType> superValueParameters = compiledValueParameters(superDescriptor);
    List<KotlinType> subValueParameters = compiledValueParameters(subDescriptor);

    List<TypeParameterDescriptor> superTypeParameters = superDescriptor.getTypeParameters();
    List<TypeParameterDescriptor> subTypeParameters = subDescriptor.getTypeParameters();

    if (superTypeParameters.size() != subTypeParameters.size()) {
      for (int i = 0; i < superValueParameters.size(); ++i) {
        // TODO: compare erasure
        if (!KotlinTypeChecker.DEFAULT.equalTypes(
            superValueParameters.get(i), subValueParameters.get(i))) {
          return OverrideCompatibilityInfo.incompatible("Type parameter number mismatch");
        }
      }
      return OverrideCompatibilityInfo.conflict("Type parameter number mismatch");
    }

    KotlinTypeChecker typeChecker = createTypeChecker(superTypeParameters, subTypeParameters);

    for (int i = 0; i < superTypeParameters.size(); i++) {
      if (!areTypeParametersEquivalent(
          superTypeParameters.get(i), subTypeParameters.get(i), typeChecker)) {
        return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch");
      }
    }

    for (int i = 0; i < superValueParameters.size(); i++) {
      if (!areTypesEquivalent(
          superValueParameters.get(i), subValueParameters.get(i), typeChecker)) {
        return OverrideCompatibilityInfo.incompatible("Value parameter type mismatch");
      }
    }

    if (checkReturnType) {
      KotlinType superReturnType = superDescriptor.getReturnType();
      KotlinType subReturnType = subDescriptor.getReturnType();

      if (superReturnType != null && subReturnType != null) {
        boolean bothErrors = subReturnType.isError() && superReturnType.isError();
        if (!bothErrors && !typeChecker.isSubtypeOf(subReturnType, superReturnType)) {
          return OverrideCompatibilityInfo.conflict("Return type mismatch");
        }
      }
    }

    return OverrideCompatibilityInfo.success();
  }

  @Nullable
  public static OverrideCompatibilityInfo getBasicOverridabilityProblem(
      @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
    if (superDescriptor instanceof FunctionDescriptor
            && !(subDescriptor instanceof FunctionDescriptor)
        || superDescriptor instanceof PropertyDescriptor
            && !(subDescriptor instanceof PropertyDescriptor)) {
      return OverrideCompatibilityInfo.incompatible("Member kind mismatch");
    }

    if (!(superDescriptor instanceof FunctionDescriptor)
        && !(superDescriptor instanceof PropertyDescriptor)) {
      throw new IllegalArgumentException(
          "This type of CallableDescriptor cannot be checked for overridability: "
              + superDescriptor);
    }

    // TODO: check outside of this method
    if (!superDescriptor.getName().equals(subDescriptor.getName())) {
      return OverrideCompatibilityInfo.incompatible("Name mismatch");
    }

    OverrideCompatibilityInfo receiverAndParameterResult =
        checkReceiverAndParameterCount(superDescriptor, subDescriptor);
    if (receiverAndParameterResult != null) {
      return receiverAndParameterResult;
    }

    return null;
  }

  @NotNull
  private KotlinTypeChecker createTypeChecker(
      @NotNull List<TypeParameterDescriptor> firstParameters,
      @NotNull List<TypeParameterDescriptor> secondParameters) {
    assert firstParameters.size() == secondParameters.size()
        : "Should be the same number of type parameters: "
            + firstParameters
            + " vs "
            + secondParameters;
    if (firstParameters.isEmpty()) return KotlinTypeCheckerImpl.withAxioms(equalityAxioms);

    final Map<TypeConstructor, TypeConstructor> matchingTypeConstructors =
        new HashMap<TypeConstructor, TypeConstructor>();
    for (int i = 0; i < firstParameters.size(); i++) {
      matchingTypeConstructors.put(
          firstParameters.get(i).getTypeConstructor(),
          secondParameters.get(i).getTypeConstructor());
    }

    return KotlinTypeCheckerImpl.withAxioms(
        new KotlinTypeChecker.TypeConstructorEquality() {
          @Override
          public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
            if (equalityAxioms.equals(a, b)) return true;
            TypeConstructor img1 = matchingTypeConstructors.get(a);
            TypeConstructor img2 = matchingTypeConstructors.get(b);
            return (img1 != null && img1.equals(b)) || (img2 != null && img2.equals(a));
          }
        });
  }

  @Nullable
  private static OverrideCompatibilityInfo checkReceiverAndParameterCount(
      CallableDescriptor superDescriptor, CallableDescriptor subDescriptor) {
    if ((superDescriptor.getExtensionReceiverParameter() == null)
        != (subDescriptor.getExtensionReceiverParameter() == null)) {
      return OverrideCompatibilityInfo.incompatible("Receiver presence mismatch");
    }

    if (superDescriptor.getValueParameters().size() != subDescriptor.getValueParameters().size()) {
      return OverrideCompatibilityInfo.incompatible("Value parameter number mismatch");
    }

    return null;
  }

  private static boolean areTypesEquivalent(
      @NotNull KotlinType typeInSuper,
      @NotNull KotlinType typeInSub,
      @NotNull KotlinTypeChecker typeChecker) {
    boolean bothErrors = typeInSuper.isError() && typeInSub.isError();
    return bothErrors || typeChecker.equalTypes(typeInSuper, typeInSub);
  }

  // See JLS 8, 8.4.4 Generic Methods
  // TODO: use TypeSubstitutor instead
  private static boolean areTypeParametersEquivalent(
      @NotNull TypeParameterDescriptor superTypeParameter,
      @NotNull TypeParameterDescriptor subTypeParameter,
      @NotNull KotlinTypeChecker typeChecker) {
    List<KotlinType> superBounds = superTypeParameter.getUpperBounds();
    List<KotlinType> subBounds = new ArrayList<KotlinType>(subTypeParameter.getUpperBounds());
    if (superBounds.size() != subBounds.size()) return false;

    outer:
    for (KotlinType superBound : superBounds) {
      ListIterator<KotlinType> it = subBounds.listIterator();
      while (it.hasNext()) {
        KotlinType subBound = it.next();
        if (areTypesEquivalent(superBound, subBound, typeChecker)) {
          it.remove();
          continue outer;
        }
      }
      return false;
    }

    return true;
  }

  private static List<KotlinType> compiledValueParameters(CallableDescriptor callableDescriptor) {
    ReceiverParameterDescriptor receiverParameter =
        callableDescriptor.getExtensionReceiverParameter();
    List<KotlinType> parameters = new ArrayList<KotlinType>();
    if (receiverParameter != null) {
      parameters.add(receiverParameter.getType());
    }
    for (ValueParameterDescriptor valueParameterDescriptor :
        callableDescriptor.getValueParameters()) {
      parameters.add(valueParameterDescriptor.getType());
    }
    return parameters;
  }

  public static void generateOverridesInFunctionGroup(
      @SuppressWarnings("UnusedParameters") @NotNull
          Name
              name, // DO NOT DELETE THIS PARAMETER: needed to make sure all descriptors have the
                    // same name
      @NotNull Collection<? extends CallableMemberDescriptor> membersFromSupertypes,
      @NotNull Collection<? extends CallableMemberDescriptor> membersFromCurrent,
      @NotNull ClassDescriptor current,
      @NotNull OverridingStrategy strategy) {
    Collection<CallableMemberDescriptor> notOverridden =
        new LinkedHashSet<CallableMemberDescriptor>(membersFromSupertypes);

    for (CallableMemberDescriptor fromCurrent : membersFromCurrent) {
      Collection<CallableMemberDescriptor> bound =
          extractAndBindOverridesForMember(fromCurrent, membersFromSupertypes, current, strategy);
      notOverridden.removeAll(bound);
    }

    createAndBindFakeOverrides(current, notOverridden, strategy);
  }

  public static boolean isVisibleForOverride(
      @NotNull MemberDescriptor overriding, @NotNull MemberDescriptor fromSuper) {
    return !Visibilities.isPrivate(fromSuper.getVisibility())
        && Visibilities.isVisibleIgnoringReceiver(fromSuper, overriding);
  }

  private static Collection<CallableMemberDescriptor> extractAndBindOverridesForMember(
      @NotNull CallableMemberDescriptor fromCurrent,
      @NotNull Collection<? extends CallableMemberDescriptor> descriptorsFromSuper,
      @NotNull ClassDescriptor current,
      @NotNull OverridingStrategy strategy) {
    Collection<CallableMemberDescriptor> bound =
        new ArrayList<CallableMemberDescriptor>(descriptorsFromSuper.size());
    Collection<CallableMemberDescriptor> overridden = SmartSet.create();
    for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) {
      OverrideCompatibilityInfo.Result result =
          DEFAULT.isOverridableBy(fromSupertype, fromCurrent, current).getResult();

      boolean isVisibleForOverride = isVisibleForOverride(fromCurrent, fromSupertype);

      switch (result) {
        case OVERRIDABLE:
          if (isVisibleForOverride) {
            overridden.add(fromSupertype);
          }
          bound.add(fromSupertype);
          break;
        case CONFLICT:
          if (isVisibleForOverride) {
            strategy.overrideConflict(fromSupertype, fromCurrent);
          }
          bound.add(fromSupertype);
          break;
        case INCOMPATIBLE:
          break;
      }
    }

    strategy.setOverriddenDescriptors(fromCurrent, overridden);

    return bound;
  }

  private static boolean allHasSameContainingDeclaration(
      @NotNull Collection<CallableMemberDescriptor> notOverridden) {
    if (notOverridden.size() < 2) return true;

    final DeclarationDescriptor containingDeclaration =
        notOverridden.iterator().next().getContainingDeclaration();
    return CollectionsKt.all(
        notOverridden,
        new Function1<CallableMemberDescriptor, Boolean>() {
          @Override
          public Boolean invoke(CallableMemberDescriptor descriptor) {
            return descriptor.getContainingDeclaration() == containingDeclaration;
          }
        });
  }

  private static void createAndBindFakeOverrides(
      @NotNull ClassDescriptor current,
      @NotNull Collection<CallableMemberDescriptor> notOverridden,
      @NotNull OverridingStrategy strategy) {
    // Optimization: If all notOverridden descriptors have the same containing declaration,
    // then we can just create fake overrides for them, because they should be matched correctly in
    // their containing declaration
    if (allHasSameContainingDeclaration(notOverridden)) {
      for (CallableMemberDescriptor descriptor : notOverridden) {
        createAndBindFakeOverride(Collections.singleton(descriptor), current, strategy);
      }
      return;
    }

    Queue<CallableMemberDescriptor> fromSuperQueue =
        new LinkedList<CallableMemberDescriptor>(notOverridden);
    while (!fromSuperQueue.isEmpty()) {
      CallableMemberDescriptor notOverriddenFromSuper =
          VisibilityUtilKt.findMemberWithMaxVisibility(fromSuperQueue);
      Collection<CallableMemberDescriptor> overridables =
          extractMembersOverridableInBothWays(notOverriddenFromSuper, fromSuperQueue, strategy);
      createAndBindFakeOverride(overridables, current, strategy);
    }
  }

  public static boolean isMoreSpecific(
      @NotNull CallableDescriptor a, @NotNull CallableDescriptor b) {
    KotlinType aReturnType = a.getReturnType();
    KotlinType bReturnType = b.getReturnType();

    assert aReturnType != null : "Return type of " + a + " is null";
    assert bReturnType != null : "Return type of " + b + " is null";

    if (!isVisibilityMoreSpecific(a, b)) return false;

    if (a instanceof SimpleFunctionDescriptor) {
      assert b instanceof SimpleFunctionDescriptor : "b is " + b.getClass();

      return isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType);
    }
    if (a instanceof PropertyDescriptor) {
      assert b instanceof PropertyDescriptor : "b is " + b.getClass();

      PropertyDescriptor pa = (PropertyDescriptor) a;
      PropertyDescriptor pb = (PropertyDescriptor) b;

      if (!isAccessorMoreSpecific(pa.getSetter(), pb.getSetter())) return false;

      if (pa.isVar() && pb.isVar()) {
        return DEFAULT
            .createTypeChecker(a.getTypeParameters(), b.getTypeParameters())
            .equalTypes(aReturnType, bReturnType);
      } else {
        // both vals or var vs val: val can't be more specific then var
        return !(!pa.isVar() && pb.isVar())
            && isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType);
      }
    }
    throw new IllegalArgumentException("Unexpected callable: " + a.getClass());
  }

  private static boolean isVisibilityMoreSpecific(
      @NotNull DeclarationDescriptorWithVisibility a,
      @NotNull DeclarationDescriptorWithVisibility b) {
    Integer result = Visibilities.compare(a.getVisibility(), b.getVisibility());
    return result == null || result >= 0;
  }

  private static boolean isAccessorMoreSpecific(
      @Nullable PropertyAccessorDescriptor a, @Nullable PropertyAccessorDescriptor b) {
    if (a == null || b == null) return true;
    return isVisibilityMoreSpecific(a, b);
  }

  private static boolean isMoreSpecificThenAllOf(
      @NotNull CallableDescriptor candidate, @NotNull Collection<CallableDescriptor> descriptors) {
    // NB subtyping relation in Kotlin is not transitive in presence of flexible types:
    //  String? <: String! <: String, but not String? <: String
    for (CallableDescriptor descriptor : descriptors) {
      if (!isMoreSpecific(candidate, descriptor)) {
        return false;
      }
    }
    return true;
  }

  private static boolean isReturnTypeMoreSpecific(
      @NotNull CallableDescriptor a,
      @NotNull KotlinType aReturnType,
      @NotNull CallableDescriptor b,
      @NotNull KotlinType bReturnType) {
    KotlinTypeChecker typeChecker =
        DEFAULT.createTypeChecker(a.getTypeParameters(), b.getTypeParameters());
    return typeChecker.isSubtypeOf(aReturnType, bReturnType);
  }

  @NotNull
  public static <H> H selectMostSpecificMember(
      @NotNull Collection<H> overridables,
      @NotNull Function1<H, CallableDescriptor> descriptorByHandle) {
    assert !overridables.isEmpty() : "Should have at least one overridable descriptor";

    if (overridables.size() == 1) {
      return CollectionsKt.first(overridables);
    }

    Collection<H> candidates = new ArrayList<H>(2);
    List<CallableDescriptor> callableMemberDescriptors =
        CollectionsKt.map(overridables, descriptorByHandle);

    H transitivelyMostSpecific = CollectionsKt.first(overridables);
    CallableDescriptor transitivelyMostSpecificDescriptor =
        descriptorByHandle.invoke(transitivelyMostSpecific);

    for (H overridable : overridables) {
      CallableDescriptor descriptor = descriptorByHandle.invoke(overridable);
      if (isMoreSpecificThenAllOf(descriptor, callableMemberDescriptors)) {
        candidates.add(overridable);
      }
      if (isMoreSpecific(descriptor, transitivelyMostSpecificDescriptor)
          && !isMoreSpecific(transitivelyMostSpecificDescriptor, descriptor)) {
        transitivelyMostSpecific = overridable;
      }
    }

    if (candidates.isEmpty()) {
      return transitivelyMostSpecific;
    } else if (candidates.size() == 1) {
      return CollectionsKt.first(candidates);
    }

    H firstNonFlexible = null;
    for (H candidate : candidates) {
      //noinspection ConstantConditions
      if (!FlexibleTypesKt.isFlexible(descriptorByHandle.invoke(candidate).getReturnType())) {
        firstNonFlexible = candidate;
        break;
      }
    }
    if (firstNonFlexible != null) {
      return firstNonFlexible;
    }

    return CollectionsKt.first(candidates);
  }

  private static void createAndBindFakeOverride(
      @NotNull Collection<CallableMemberDescriptor> overridables,
      @NotNull ClassDescriptor current,
      @NotNull OverridingStrategy strategy) {
    Collection<CallableMemberDescriptor> visibleOverridables =
        filterVisibleFakeOverrides(current, overridables);
    boolean allInvisible = visibleOverridables.isEmpty();
    Collection<CallableMemberDescriptor> effectiveOverridden =
        allInvisible ? overridables : visibleOverridables;

    Modality modality = determineModality(effectiveOverridden);
    Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED;

    // FIXME doesn't work as expected for flexible types: should create a refined signature.
    // Current algorithm produces bad results in presence of annotated Java signatures such as:
    //      J: foo(s: String!): String -- @NotNull String foo(String s);
    //      K: foo(s: String): String?
    //  --> 'foo(s: String!): String' as an inherited signature with most specific return type.
    // This is bad because it can be overridden by 'foo(s: String?): String', which is not
    // override-equivalent with K::foo above.
    // Should be 'foo(s: String): String'.
    CallableMemberDescriptor mostSpecific =
        selectMostSpecificMember(
            effectiveOverridden,
            new Function1<CallableMemberDescriptor, CallableDescriptor>() {
              @Override
              public CallableMemberDescriptor invoke(CallableMemberDescriptor descriptor) {
                return descriptor;
              }
            });
    CallableMemberDescriptor fakeOverride =
        mostSpecific.copy(
            current, modality, visibility, CallableMemberDescriptor.Kind.FAKE_OVERRIDE, false);
    strategy.setOverriddenDescriptors(fakeOverride, effectiveOverridden);
    assert !fakeOverride.getOverriddenDescriptors().isEmpty()
        : "Overridden descriptors should be set for " + CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
    strategy.addFakeOverride(fakeOverride);
  }

  @NotNull
  private static Modality determineModality(
      @NotNull Collection<CallableMemberDescriptor> descriptors) {
    // Optimization: avoid creating hash sets in frequent cases when modality can be computed
    // trivially
    boolean hasOpen = false;
    boolean hasAbstract = false;
    for (CallableMemberDescriptor descriptor : descriptors) {
      switch (descriptor.getModality()) {
        case FINAL:
          return Modality.FINAL;
        case SEALED:
          throw new IllegalStateException("Member cannot have SEALED modality: " + descriptor);
        case OPEN:
          hasOpen = true;
          break;
        case ABSTRACT:
          hasAbstract = true;
          break;
      }
    }

    if (hasOpen && !hasAbstract) return Modality.OPEN;
    if (!hasOpen && hasAbstract) return Modality.ABSTRACT;

    Set<CallableMemberDescriptor> allOverriddenDeclarations =
        new HashSet<CallableMemberDescriptor>();
    for (CallableMemberDescriptor descriptor : descriptors) {
      allOverriddenDeclarations.addAll(getOverriddenDeclarations(descriptor));
    }
    return getMinimalModality(filterOutOverridden(allOverriddenDeclarations));
  }

  @NotNull
  private static Modality getMinimalModality(
      @NotNull Collection<CallableMemberDescriptor> descriptors) {
    Modality modality = Modality.ABSTRACT;
    for (CallableMemberDescriptor descriptor : descriptors) {
      if (descriptor.getModality().compareTo(modality) < 0) {
        modality = descriptor.getModality();
      }
    }
    return modality;
  }

  @NotNull
  private static Collection<CallableMemberDescriptor> filterVisibleFakeOverrides(
      @NotNull final ClassDescriptor current,
      @NotNull Collection<CallableMemberDescriptor> toFilter) {
    return CollectionsKt.filter(
        toFilter,
        new Function1<CallableMemberDescriptor, Boolean>() {
          @Override
          public Boolean invoke(CallableMemberDescriptor descriptor) {
            // nested class could capture private member, so check for private visibility added
            return !Visibilities.isPrivate(descriptor.getVisibility())
                && Visibilities.isVisibleIgnoringReceiver(descriptor, current);
          }
        });
  }

  /**
   * @param <H> is something that handles CallableDescriptor inside
   * @return
   */
  @NotNull
  public static <H> Collection<H> extractMembersOverridableInBothWays(
      @NotNull H overrider,
      @NotNull @Mutable Collection<H> extractFrom,
      @NotNull Function1<H, CallableDescriptor> descriptorByHandle,
      @NotNull Function1<H, Unit> onConflict) {
    Collection<H> overridable = new ArrayList<H>();
    overridable.add(overrider);
    CallableDescriptor overriderDescriptor = descriptorByHandle.invoke(overrider);
    for (Iterator<H> iterator = extractFrom.iterator(); iterator.hasNext(); ) {
      H candidate = iterator.next();
      CallableDescriptor candidateDescriptor = descriptorByHandle.invoke(candidate);
      if (overrider == candidate) {
        iterator.remove();
        continue;
      }

      OverrideCompatibilityInfo.Result finalResult =
          getBothWaysOverridability(overriderDescriptor, candidateDescriptor);

      if (finalResult == OVERRIDABLE) {
        overridable.add(candidate);
        iterator.remove();
      } else if (finalResult == CONFLICT) {
        onConflict.invoke(candidate);
        iterator.remove();
      }
    }
    return overridable;
  }

  @Nullable
  public static OverrideCompatibilityInfo.Result getBothWaysOverridability(
      CallableDescriptor overriderDescriptor, CallableDescriptor candidateDescriptor) {
    OverrideCompatibilityInfo.Result result1 =
        DEFAULT.isOverridableBy(candidateDescriptor, overriderDescriptor, null).getResult();
    OverrideCompatibilityInfo.Result result2 =
        DEFAULT.isOverridableBy(overriderDescriptor, candidateDescriptor, null).getResult();

    return result1 == OVERRIDABLE && result2 == OVERRIDABLE
        ? OVERRIDABLE
        : ((result1 == CONFLICT || result2 == CONFLICT) ? CONFLICT : INCOMPATIBLE);
  }

  @NotNull
  private static Collection<CallableMemberDescriptor> extractMembersOverridableInBothWays(
      @NotNull final CallableMemberDescriptor overrider,
      @NotNull Queue<CallableMemberDescriptor> extractFrom,
      @NotNull final OverridingStrategy strategy) {
    return extractMembersOverridableInBothWays(
        overrider,
        extractFrom,
        // ID
        new Function1<CallableMemberDescriptor, CallableDescriptor>() {
          @Override
          public CallableDescriptor invoke(CallableMemberDescriptor descriptor) {
            return descriptor;
          }
        },
        new Function1<CallableMemberDescriptor, Unit>() {
          @Override
          public Unit invoke(CallableMemberDescriptor descriptor) {
            strategy.inheritanceConflict(overrider, descriptor);
            return Unit.INSTANCE;
          }
        });
  }

  public static void resolveUnknownVisibilityForMember(
      @NotNull CallableMemberDescriptor memberDescriptor,
      @Nullable Function1<CallableMemberDescriptor, Unit> cannotInferVisibility) {
    for (CallableMemberDescriptor descriptor : memberDescriptor.getOverriddenDescriptors()) {
      if (descriptor.getVisibility() == Visibilities.INHERITED) {
        resolveUnknownVisibilityForMember(descriptor, cannotInferVisibility);
      }
    }

    if (memberDescriptor.getVisibility() != Visibilities.INHERITED) {
      return;
    }

    Visibility maxVisibility = computeVisibilityToInherit(memberDescriptor);
    Visibility visibilityToInherit;
    if (maxVisibility == null) {
      if (cannotInferVisibility != null) {
        cannotInferVisibility.invoke(memberDescriptor);
      }
      visibilityToInherit = Visibilities.PUBLIC;
    } else {
      visibilityToInherit = maxVisibility;
    }

    if (memberDescriptor instanceof PropertyDescriptorImpl) {
      ((PropertyDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
      for (PropertyAccessorDescriptor accessor :
          ((PropertyDescriptor) memberDescriptor).getAccessors()) {
        // If we couldn't infer visibility for property, the diagnostic is already reported, no need
        // to report it again on accessors
        resolveUnknownVisibilityForMember(
            accessor, maxVisibility == null ? null : cannotInferVisibility);
      }
    } else if (memberDescriptor instanceof FunctionDescriptorImpl) {
      ((FunctionDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
    } else {
      assert memberDescriptor instanceof PropertyAccessorDescriptorImpl;
      ((PropertyAccessorDescriptorImpl) memberDescriptor).setVisibility(visibilityToInherit);
    }
  }

  @Nullable
  private static Visibility computeVisibilityToInherit(
      @NotNull CallableMemberDescriptor memberDescriptor) {
    Collection<? extends CallableMemberDescriptor> overriddenDescriptors =
        memberDescriptor.getOverriddenDescriptors();
    Visibility maxVisibility = findMaxVisibility(overriddenDescriptors);
    if (maxVisibility == null) {
      return null;
    }
    if (memberDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
      for (CallableMemberDescriptor overridden : overriddenDescriptors) {
        // An implementation (a non-abstract overridden member) of a fake override should have the
        // maximum possible visibility
        if (overridden.getModality() != Modality.ABSTRACT
            && !overridden.getVisibility().equals(maxVisibility)) {
          return null;
        }
      }
      return maxVisibility;
    }
    return maxVisibility.normalize();
  }

  @Nullable
  public static Visibility findMaxVisibility(
      @NotNull Collection<? extends CallableMemberDescriptor> descriptors) {
    if (descriptors.isEmpty()) {
      return Visibilities.DEFAULT_VISIBILITY;
    }
    Visibility maxVisibility = null;
    for (CallableMemberDescriptor descriptor : descriptors) {
      Visibility visibility = descriptor.getVisibility();
      assert visibility != Visibilities.INHERITED
          : "Visibility should have been computed for " + descriptor;
      if (maxVisibility == null) {
        maxVisibility = visibility;
        continue;
      }
      Integer compareResult = Visibilities.compare(visibility, maxVisibility);
      if (compareResult == null) {
        maxVisibility = null;
      } else if (compareResult > 0) {
        maxVisibility = visibility;
      }
    }
    if (maxVisibility == null) {
      return null;
    }
    for (CallableMemberDescriptor descriptor : descriptors) {
      Integer compareResult = Visibilities.compare(maxVisibility, descriptor.getVisibility());
      if (compareResult == null || compareResult < 0) {
        return null;
      }
    }
    return maxVisibility;
  }

  public static class OverrideCompatibilityInfo {
    public enum Result {
      OVERRIDABLE,
      INCOMPATIBLE,
      CONFLICT,
    }

    private static final OverrideCompatibilityInfo SUCCESS =
        new OverrideCompatibilityInfo(OVERRIDABLE, "SUCCESS");

    @NotNull
    public static OverrideCompatibilityInfo success() {
      return SUCCESS;
    }

    @NotNull
    public static OverrideCompatibilityInfo incompatible(@NotNull String debugMessage) {
      return new OverrideCompatibilityInfo(INCOMPATIBLE, debugMessage);
    }

    @NotNull
    public static OverrideCompatibilityInfo conflict(@NotNull String debugMessage) {
      return new OverrideCompatibilityInfo(CONFLICT, debugMessage);
    }

    private final Result overridable;
    private final String debugMessage;

    public OverrideCompatibilityInfo(@NotNull Result success, @NotNull String debugMessage) {
      this.overridable = success;
      this.debugMessage = debugMessage;
    }

    @NotNull
    public Result getResult() {
      return overridable;
    }

    @NotNull
    public String getDebugMessage() {
      return debugMessage;
    }
  }
}
  @Override
  public void onFirstRun(
      ConfigurationFromContext configuration,
      ConfigurationContext context,
      Runnable startRunnable) {
    KtNamedDeclaration declarationToRun = getDeclarationToRun(configuration.getSourceElement());
    final PsiNamedElement lightElement =
        CollectionsKt.firstOrNull(LightClassUtilsKt.toLightElements(declarationToRun));

    // Copied from TestNGInClassConfigurationProducer.onFirstRun()
    if (lightElement instanceof PsiMethod || lightElement instanceof PsiClass) {
      PsiMethod psiMethod;
      PsiClass containingClass;

      if (lightElement instanceof PsiMethod) {
        psiMethod = (PsiMethod) lightElement;
        containingClass = psiMethod.getContainingClass();
      } else {
        psiMethod = null;
        containingClass = (PsiClass) lightElement;
      }

      InheritorChooser inheritorChooser =
          new InheritorChooser() {
            @Override
            protected void runForClasses(
                List<PsiClass> classes,
                PsiMethod method,
                ConfigurationContext context,
                Runnable performRunnable) {
              ((TestNGConfiguration) context.getConfiguration().getConfiguration())
                  .bePatternConfiguration(classes, method);
              super.runForClasses(classes, method, context, performRunnable);
            }

            @Override
            protected void runForClass(
                PsiClass aClass,
                PsiMethod psiMethod,
                ConfigurationContext context,
                Runnable performRunnable) {
              if (lightElement instanceof PsiMethod) {
                Project project = psiMethod.getProject();
                MethodLocation methodLocation =
                    new MethodLocation(project, psiMethod, PsiLocation.fromPsiElement(aClass));
                ((TestNGConfiguration) context.getConfiguration().getConfiguration())
                    .setMethodConfiguration(methodLocation);
              } else {
                ((TestNGConfiguration) context.getConfiguration().getConfiguration())
                    .setClassConfiguration(aClass);
              }
              super.runForClass(aClass, psiMethod, context, performRunnable);
            }
          };
      if (inheritorChooser.runMethodInAbstractClass(
          context,
          startRunnable,
          psiMethod,
          containingClass,
          new Condition<PsiClass>() {
            @Override
            public boolean value(PsiClass aClass) {
              return aClass.hasModifierProperty(PsiModifier.ABSTRACT) && TestNGUtil.hasTest(aClass);
            }
          })) return;
    }

    super.onFirstRun(configuration, context, startRunnable);
  }