public class OverridingUtil { private static final List<ExternalOverridabilityCondition> EXTERNAL_CONDITIONS = KotlinPackage.toList( ServiceLoader.load( ExternalOverridabilityCondition.class, ExternalOverridabilityCondition.class.getClassLoader())); public static final OverridingUtil DEFAULT = new OverridingUtil( new JetTypeChecker.TypeConstructorEquality() { @Override public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) { return a.equals(b); } }); @NotNull public static OverridingUtil createWithEqualityAxioms( @NotNull JetTypeChecker.TypeConstructorEquality equalityAxioms) { return new OverridingUtil(equalityAxioms); } private final JetTypeChecker.TypeConstructorEquality equalityAxioms; private OverridingUtil(JetTypeChecker.TypeConstructorEquality axioms) { equalityAxioms = axioms; } @NotNull public OverrideCompatibilityInfo isOverridableBy( @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) { return isOverridableBy(superDescriptor, subDescriptor, false); } @NotNull public OverrideCompatibilityInfo isOverridableByIncludingReturnType( @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) { return isOverridableBy(superDescriptor, subDescriptor, true); } @NotNull private OverrideCompatibilityInfo isOverridableBy( @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor, boolean checkReturnType) { if (superDescriptor instanceof FunctionDescriptor) { if (!(subDescriptor instanceof FunctionDescriptor)) return OverrideCompatibilityInfo.memberKindMismatch(); } else if (superDescriptor instanceof PropertyDescriptor) { if (!(subDescriptor instanceof PropertyDescriptor)) return OverrideCompatibilityInfo.memberKindMismatch(); } else { 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.nameMismatch(); } OverrideCompatibilityInfo receiverAndParameterResult = checkReceiverAndParameterCount(superDescriptor, subDescriptor); if (receiverAndParameterResult != null) { return receiverAndParameterResult; } List<JetType> superValueParameters = compiledValueParameters(superDescriptor); List<JetType> 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) { JetType superValueParameterType = getUpperBound(superValueParameters.get(i)); JetType subValueParameterType = getUpperBound(subValueParameters.get(i)); // TODO: compare erasure if (!JetTypeChecker.DEFAULT.equalTypes(superValueParameterType, subValueParameterType)) { return OverrideCompatibilityInfo.typeParameterNumberMismatch(); } } return OverrideCompatibilityInfo.valueParameterTypeMismatch( null, null, OverrideCompatibilityInfo.Result.CONFLICT); } final Map<TypeConstructor, TypeConstructor> matchingTypeConstructors = new HashMap<TypeConstructor, TypeConstructor>(); for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) { TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i); TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i); matchingTypeConstructors.put( superTypeParameter.getTypeConstructor(), subTypeParameter.getTypeConstructor()); } JetTypeChecker.TypeConstructorEquality localEqualityAxioms = new JetTypeChecker.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); if (!(img1 != null && img1.equals(b)) && !(img2 != null && img2.equals(a))) { return false; } return true; } }; for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) { TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i); TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i); if (!areTypesEquivalent( superTypeParameter.getUpperBoundsAsType(), subTypeParameter.getUpperBoundsAsType(), localEqualityAxioms)) { return OverrideCompatibilityInfo.boundsMismatch(superTypeParameter, subTypeParameter); } } for (int i = 0, unsubstitutedValueParametersSize = superValueParameters.size(); i < unsubstitutedValueParametersSize; i++) { JetType superValueParameter = superValueParameters.get(i); JetType subValueParameter = subValueParameters.get(i); if (!areTypesEquivalent(superValueParameter, subValueParameter, localEqualityAxioms)) { return OverrideCompatibilityInfo.valueParameterTypeMismatch( superValueParameter, subValueParameter, INCOMPATIBLE); } } if (checkReturnType) { JetType superReturnType = superDescriptor.getReturnType(); JetType subReturnType = subDescriptor.getReturnType(); if (superReturnType != null && subReturnType != null) { boolean bothErrors = subReturnType.isError() && superReturnType.isError(); if (!bothErrors && !JetTypeChecker.withAxioms(localEqualityAxioms) .isSubtypeOf(subReturnType, superReturnType)) { return OverrideCompatibilityInfo.returnTypeMismatch(superReturnType, subReturnType); } } } for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) { if (!externalCondition.isOverridable(superDescriptor, subDescriptor)) { return OverrideCompatibilityInfo.externalConditionFailed(externalCondition.getClass()); } } return OverrideCompatibilityInfo.success(); } @Nullable static OverrideCompatibilityInfo checkReceiverAndParameterCount( CallableDescriptor superDescriptor, CallableDescriptor subDescriptor) { if ((superDescriptor.getExtensionReceiverParameter() == null) != (subDescriptor.getExtensionReceiverParameter() == null)) { return OverrideCompatibilityInfo.receiverPresenceMismatch(); } if (superDescriptor.getValueParameters().size() != subDescriptor.getValueParameters().size()) { return OverrideCompatibilityInfo.valueParameterNumberMismatch(); } return null; } private static boolean areTypesEquivalent( @NotNull JetType typeInSuper, @NotNull JetType typeInSub, @NotNull JetTypeChecker.TypeConstructorEquality axioms) { boolean bothErrors = typeInSuper.isError() && typeInSub.isError(); if (!bothErrors && !JetTypeChecker.withAxioms(axioms).equalTypes(typeInSuper, typeInSub)) { return false; } return true; } static List<JetType> compiledValueParameters(CallableDescriptor callableDescriptor) { ReceiverParameterDescriptor receiverParameter = callableDescriptor.getExtensionReceiverParameter(); ArrayList<JetType> parameters = new ArrayList<JetType>(); if (receiverParameter != null) { parameters.add(receiverParameter.getType()); } for (ValueParameterDescriptor valueParameterDescriptor : callableDescriptor.getValueParameters()) { parameters.add(valueParameterDescriptor.getType()); } return parameters; } static JetType getUpperBound(JetType type) { if (type.getConstructor().getDeclarationDescriptor() instanceof ClassDescriptor) { return type; } else if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) { return ((TypeParameterDescriptor) type.getConstructor().getDeclarationDescriptor()) .getUpperBoundsAsType(); } else { throw new IllegalStateException( "unknown type constructor: " + type.getConstructor().getClass().getName()); } } public static void bindOverride( CallableMemberDescriptor fromCurrent, CallableMemberDescriptor fromSupertype) { fromCurrent.addOverriddenDescriptor(fromSupertype); for (ValueParameterDescriptor parameterFromCurrent : fromCurrent.getValueParameters()) { assert parameterFromCurrent.getIndex() < fromSupertype.getValueParameters().size() : "An override relation between functions implies that they have the same number of value parameters"; ValueParameterDescriptor parameterFromSupertype = fromSupertype.getValueParameters().get(parameterFromCurrent.getIndex()); parameterFromCurrent.addOverriddenDescriptor(parameterFromSupertype); } } 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 DescriptorSink sink) { Collection<CallableMemberDescriptor> notOverridden = new LinkedHashSet<CallableMemberDescriptor>(membersFromSupertypes); for (CallableMemberDescriptor fromCurrent : membersFromCurrent) { Collection<CallableMemberDescriptor> bound = extractAndBindOverridesForMember(fromCurrent, membersFromSupertypes, current, sink); notOverridden.removeAll(bound); } createAndBindFakeOverrides(current, notOverridden, sink); } private static Collection<CallableMemberDescriptor> extractAndBindOverridesForMember( @NotNull CallableMemberDescriptor fromCurrent, @NotNull Collection<? extends CallableMemberDescriptor> descriptorsFromSuper, @NotNull ClassDescriptor current, @NotNull DescriptorSink sink) { Collection<CallableMemberDescriptor> bound = new ArrayList<CallableMemberDescriptor>(descriptorsFromSuper.size()); for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) { OverrideCompatibilityInfo.Result result = DEFAULT.isOverridableBy(fromSupertype, fromCurrent).getResult(); boolean isVisible = Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, fromSupertype, current); switch (result) { case OVERRIDABLE: if (isVisible) { bindOverride(fromCurrent, fromSupertype); } bound.add(fromSupertype); break; case CONFLICT: if (isVisible) { sink.conflict(fromSupertype, fromCurrent); } bound.add(fromSupertype); break; case INCOMPATIBLE: break; } } return bound; } private static void createAndBindFakeOverrides( @NotNull ClassDescriptor current, @NotNull Collection<CallableMemberDescriptor> notOverridden, @NotNull DescriptorSink sink) { Queue<CallableMemberDescriptor> fromSuperQueue = new LinkedList<CallableMemberDescriptor>(notOverridden); while (!fromSuperQueue.isEmpty()) { CallableMemberDescriptor notOverriddenFromSuper = VisibilityUtil.findMemberWithMaxVisibility(fromSuperQueue); Collection<CallableMemberDescriptor> overridables = extractMembersOverridableInBothWays(notOverriddenFromSuper, fromSuperQueue, sink); createAndBindFakeOverride(overridables, current, sink); } } private static boolean isMoreSpecific( @NotNull CallableMemberDescriptor a, @NotNull CallableMemberDescriptor b) { if (a instanceof SimpleFunctionDescriptor) { assert b instanceof SimpleFunctionDescriptor : "b is " + b.getClass(); JetType aReturnType = a.getReturnType(); assert aReturnType != null; JetType bReturnType = b.getReturnType(); assert bReturnType != null; return JetTypeChecker.DEFAULT.isSubtypeOf(aReturnType, bReturnType); } if (a instanceof PropertyDescriptor) { assert b instanceof PropertyDescriptor : "b is " + b.getClass(); if (((PropertyDescriptor) a).isVar() || ((PropertyDescriptor) b).isVar()) { return ((PropertyDescriptor) a).isVar(); } // both vals return JetTypeChecker.DEFAULT.isSubtypeOf( ((PropertyDescriptor) a).getType(), ((PropertyDescriptor) b).getType()); } throw new IllegalArgumentException("Unexpected callable: " + a.getClass()); } private static CallableMemberDescriptor selectMostSpecificMemberFromSuper( @NotNull Collection<CallableMemberDescriptor> overridables) { CallableMemberDescriptor result = null; for (CallableMemberDescriptor overridable : overridables) { if (result == null || isMoreSpecific(overridable, result)) { result = overridable; } } return result; } private static void createAndBindFakeOverride( @NotNull Collection<CallableMemberDescriptor> overridables, @NotNull ClassDescriptor current, @NotNull DescriptorSink sink) { Collection<CallableMemberDescriptor> visibleOverridables = filterVisibleFakeOverrides(current, overridables); boolean allInvisible = visibleOverridables.isEmpty(); Collection<CallableMemberDescriptor> effectiveOverridden = allInvisible ? overridables : visibleOverridables; Modality modality = getMinimalModality(effectiveOverridden); Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED; CallableMemberDescriptor mostSpecific = selectMostSpecificMemberFromSuper(effectiveOverridden); CallableMemberDescriptor fakeOverride = mostSpecific.copy( current, modality, visibility, CallableMemberDescriptor.Kind.FAKE_OVERRIDE, false); for (CallableMemberDescriptor descriptor : effectiveOverridden) { bindOverride(fakeOverride, descriptor); } sink.addToScope(fakeOverride); } @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 KotlinPackage.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.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, descriptor, current); } }); } @NotNull private static Collection<CallableMemberDescriptor> extractMembersOverridableInBothWays( @NotNull CallableMemberDescriptor overrider, @NotNull Queue<CallableMemberDescriptor> extractFrom, @NotNull DescriptorSink sink) { Collection<CallableMemberDescriptor> overridable = new ArrayList<CallableMemberDescriptor>(); overridable.add(overrider); for (Iterator<CallableMemberDescriptor> iterator = extractFrom.iterator(); iterator.hasNext(); ) { CallableMemberDescriptor candidate = iterator.next(); if (overrider == candidate) { iterator.remove(); continue; } OverrideCompatibilityInfo.Result result1 = DEFAULT.isOverridableBy(candidate, overrider).getResult(); OverrideCompatibilityInfo.Result result2 = DEFAULT.isOverridableBy(overrider, candidate).getResult(); if (result1 == OVERRIDABLE && result2 == OVERRIDABLE) { overridable.add(candidate); iterator.remove(); } else if (result1 == CONFLICT || result2 == CONFLICT) { sink.conflict(overrider, candidate); iterator.remove(); } } return overridable; } 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) { Set<? 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 private static Visibility findMaxVisibility( @NotNull Collection<? extends CallableMemberDescriptor> descriptors) { if (descriptors.isEmpty()) { return Visibilities.INTERNAL; } 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; } } // TODO: IDEA seems to issue an incorrect warning here //noinspection ConstantConditions 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; } @NotNull @KotlinSignature( "fun getTopmostOverridenDescriptors(originalDescriptor: CallableDescriptor): List<out CallableDescriptor>") public static List<? extends CallableDescriptor> getTopmostOverridenDescriptors( @NotNull CallableDescriptor originalDescriptor) { return DFS.dfs( Collections.singletonList(originalDescriptor), new DFS.Neighbors<CallableDescriptor>() { @NotNull @Override public Iterable<? extends CallableDescriptor> getNeighbors(CallableDescriptor current) { return current.getOverriddenDescriptors(); } }, new DFS.CollectingNodeHandler< CallableDescriptor, CallableDescriptor, ArrayList<CallableDescriptor>>( new ArrayList<CallableDescriptor>()) { @Override public void afterChildren(CallableDescriptor current) { if (current.getOverriddenDescriptors().isEmpty()) { result.add(current); } } }); } public static boolean traverseOverridenDescriptors( @NotNull CallableDescriptor originalDescriptor, @NotNull final Function1<CallableDescriptor, Boolean> handler) { return DFS.dfs( Collections.singletonList(originalDescriptor), new DFS.Neighbors<CallableDescriptor>() { @NotNull @Override public Iterable<? extends CallableDescriptor> getNeighbors(CallableDescriptor current) { return current.getOverriddenDescriptors(); } }, new DFS.AbstractNodeHandler<CallableDescriptor, Boolean>() { private boolean result = true; @Override public boolean beforeChildren(CallableDescriptor current) { if (!handler.invoke(current)) { result = false; } return result; } @Override public Boolean result() { return result; } }); } public interface DescriptorSink { void addToScope(@NotNull CallableMemberDescriptor fakeOverride); void conflict( @NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent); } public static class OverrideCompatibilityInfo { public enum Result { OVERRIDABLE, INCOMPATIBLE, CONFLICT, } private static final OverrideCompatibilityInfo SUCCESS = new OverrideCompatibilityInfo(Result.OVERRIDABLE, "SUCCESS"); @NotNull public static OverrideCompatibilityInfo success() { return SUCCESS; } @NotNull public static OverrideCompatibilityInfo nameMismatch() { return new OverrideCompatibilityInfo(INCOMPATIBLE, "nameMismatch"); // TODO } @NotNull public static OverrideCompatibilityInfo typeParameterNumberMismatch() { return new OverrideCompatibilityInfo(INCOMPATIBLE, "typeParameterNumberMismatch"); // TODO } @NotNull public static OverrideCompatibilityInfo receiverPresenceMismatch() { return new OverrideCompatibilityInfo(INCOMPATIBLE, "receiverPresenceMismatch"); // TODO } @NotNull public static OverrideCompatibilityInfo valueParameterNumberMismatch() { return new OverrideCompatibilityInfo(INCOMPATIBLE, "valueParameterNumberMismatch"); // TODO } @NotNull public static OverrideCompatibilityInfo boundsMismatch( TypeParameterDescriptor superTypeParameter, TypeParameterDescriptor subTypeParameter) { return new OverrideCompatibilityInfo(INCOMPATIBLE, "boundsMismatch"); // TODO } @NotNull public static OverrideCompatibilityInfo valueParameterTypeMismatch( JetType superValueParameter, JetType subValueParameter, Result result) { return new OverrideCompatibilityInfo(result, "valueParameterTypeMismatch"); // TODO } @NotNull public static OverrideCompatibilityInfo memberKindMismatch() { return new OverrideCompatibilityInfo(INCOMPATIBLE, "memberKindMismatch"); // TODO } @NotNull public static OverrideCompatibilityInfo returnTypeMismatch( JetType substitutedSuperReturnType, JetType unsubstitutedSubReturnType) { return new OverrideCompatibilityInfo( Result.CONFLICT, "returnTypeMismatch: " + unsubstitutedSubReturnType + " >< " + substitutedSuperReturnType); // TODO } @NotNull public static OverrideCompatibilityInfo varOverriddenByVal() { return new OverrideCompatibilityInfo(INCOMPATIBLE, "varOverriddenByVal"); // TODO } @NotNull public static OverrideCompatibilityInfo externalConditionFailed( Class<? extends ExternalOverridabilityCondition> conditionClass) { return new OverrideCompatibilityInfo( INCOMPATIBLE, "externalConditionFailed: " + conditionClass.getName()); // TODO } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// private final Result overridable; private final String message; public OverrideCompatibilityInfo(Result success, String message) { this.overridable = success; this.message = message; } public Result getResult() { return overridable; } public String getMessage() { return message; } } }
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; } @NotNull public OverrideCompatibilityInfo isOverridableBy( @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) { return isOverridableBy(superDescriptor, subDescriptor, false); } @NotNull public OverrideCompatibilityInfo isOverridableByIncludingReturnType( @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) { return isOverridableBy(superDescriptor, subDescriptor, true); } @NotNull private OverrideCompatibilityInfo isOverridableBy( @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor, boolean checkReturnType) { for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) { ExternalOverridabilityCondition.Result result = externalCondition.isOverridable(superDescriptor, subDescriptor); switch (result) { case OVERRIDABLE: return OverrideCompatibilityInfo.success(); 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 } } return isOverridableByWithoutExternalConditions( superDescriptor, subDescriptor, checkReturnType); } @NotNull public OverrideCompatibilityInfo isOverridableByWithoutExternalConditions( @NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor, boolean checkReturnType) { 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; } 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(); } @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 KotlinTypeChecker.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 KotlinTypeChecker.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 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; } 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 DescriptorSink sink) { Collection<CallableMemberDescriptor> notOverridden = new LinkedHashSet<CallableMemberDescriptor>(membersFromSupertypes); for (CallableMemberDescriptor fromCurrent : membersFromCurrent) { Collection<CallableMemberDescriptor> bound = extractAndBindOverridesForMember(fromCurrent, membersFromSupertypes, current, sink); notOverridden.removeAll(bound); } createAndBindFakeOverrides(current, notOverridden, sink); } private static Collection<CallableMemberDescriptor> extractAndBindOverridesForMember( @NotNull CallableMemberDescriptor fromCurrent, @NotNull Collection<? extends CallableMemberDescriptor> descriptorsFromSuper, @NotNull ClassDescriptor current, @NotNull DescriptorSink sink) { Collection<CallableMemberDescriptor> bound = new ArrayList<CallableMemberDescriptor>(descriptorsFromSuper.size()); for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) { OverrideCompatibilityInfo.Result result = DEFAULT.isOverridableBy(fromSupertype, fromCurrent).getResult(); boolean isVisible = Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, fromSupertype, current); switch (result) { case OVERRIDABLE: if (isVisible) { fromCurrent.addOverriddenDescriptor(fromSupertype); } bound.add(fromSupertype); break; case CONFLICT: if (isVisible) { sink.conflict(fromSupertype, fromCurrent); } bound.add(fromSupertype); break; case INCOMPATIBLE: break; } } return bound; } private static void createAndBindFakeOverrides( @NotNull ClassDescriptor current, @NotNull Collection<CallableMemberDescriptor> notOverridden, @NotNull DescriptorSink sink) { Queue<CallableMemberDescriptor> fromSuperQueue = new LinkedList<CallableMemberDescriptor>(notOverridden); while (!fromSuperQueue.isEmpty()) { CallableMemberDescriptor notOverriddenFromSuper = VisibilityUtilKt.findMemberWithMaxVisibility(fromSuperQueue); Collection<CallableMemberDescriptor> overridables = extractMembersOverridableInBothWays(notOverriddenFromSuper, fromSuperQueue, sink); createAndBindFakeOverride(overridables, current, sink); } } private static boolean isMoreSpecific( @NotNull CallableMemberDescriptor a, @NotNull CallableMemberDescriptor b) { if (a instanceof SimpleFunctionDescriptor) { assert b instanceof SimpleFunctionDescriptor : "b is " + b.getClass(); KotlinType aReturnType = a.getReturnType(); assert aReturnType != null; KotlinType bReturnType = b.getReturnType(); assert bReturnType != null; return KotlinTypeChecker.DEFAULT.isSubtypeOf(aReturnType, bReturnType); } if (a instanceof PropertyDescriptor) { assert b instanceof PropertyDescriptor : "b is " + b.getClass(); if (((PropertyDescriptor) a).isVar() || ((PropertyDescriptor) b).isVar()) { return ((PropertyDescriptor) a).isVar(); } // both vals return KotlinTypeChecker.DEFAULT.isSubtypeOf( ((PropertyDescriptor) a).getType(), ((PropertyDescriptor) b).getType()); } throw new IllegalArgumentException("Unexpected callable: " + a.getClass()); } private static CallableMemberDescriptor selectMostSpecificMemberFromSuper( @NotNull Collection<CallableMemberDescriptor> overridables) { CallableMemberDescriptor result = null; for (CallableMemberDescriptor overridable : overridables) { if (result == null || isMoreSpecific(overridable, result)) { result = overridable; } } return result; } private static void createAndBindFakeOverride( @NotNull Collection<CallableMemberDescriptor> overridables, @NotNull ClassDescriptor current, @NotNull DescriptorSink sink) { Collection<CallableMemberDescriptor> visibleOverridables = filterVisibleFakeOverrides(current, overridables); boolean allInvisible = visibleOverridables.isEmpty(); Collection<CallableMemberDescriptor> effectiveOverridden = allInvisible ? overridables : visibleOverridables; Modality modality = getMinimalModality(effectiveOverridden); Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED; CallableMemberDescriptor mostSpecific = selectMostSpecificMemberFromSuper(effectiveOverridden); CallableMemberDescriptor fakeOverride = mostSpecific.copy( current, modality, visibility, CallableMemberDescriptor.Kind.FAKE_OVERRIDE, false); for (CallableMemberDescriptor descriptor : effectiveOverridden) { fakeOverride.addOverriddenDescriptor(descriptor); } sink.addFakeOverride(fakeOverride); } @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.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, descriptor, current); } }); } @NotNull private static Collection<CallableMemberDescriptor> extractMembersOverridableInBothWays( @NotNull CallableMemberDescriptor overrider, @NotNull Queue<CallableMemberDescriptor> extractFrom, @NotNull DescriptorSink sink) { Collection<CallableMemberDescriptor> overridable = new ArrayList<CallableMemberDescriptor>(); overridable.add(overrider); for (Iterator<CallableMemberDescriptor> iterator = extractFrom.iterator(); iterator.hasNext(); ) { CallableMemberDescriptor candidate = iterator.next(); if (overrider == candidate) { iterator.remove(); continue; } OverrideCompatibilityInfo.Result result1 = DEFAULT.isOverridableBy(candidate, overrider).getResult(); OverrideCompatibilityInfo.Result result2 = DEFAULT.isOverridableBy(overrider, candidate).getResult(); if (result1 == OVERRIDABLE && result2 == OVERRIDABLE) { overridable.add(candidate); iterator.remove(); } else if (result1 == CONFLICT || result2 == CONFLICT) { sink.conflict(overrider, candidate); iterator.remove(); } } return overridable; } 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 interface DescriptorSink { void addFakeOverride(@NotNull CallableMemberDescriptor fakeOverride); void conflict( @NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent); } 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; } } }
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; } } }