Java tutorial
/* * Copyright 2010-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.napile.compiler.lang.resolve.processors; import static org.napile.compiler.lang.diagnostics.Errors.*; import java.util.*; import javax.inject.Inject; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.napile.asm.resolve.name.Name; import org.napile.compiler.lang.descriptors.*; import org.napile.compiler.lang.diagnostics.Errors; import org.napile.compiler.lang.lexer.NapileTokens; import org.napile.compiler.lang.psi.NapileAnonymClass; import org.napile.compiler.lang.psi.NapileCallParameterAsVariable; import org.napile.compiler.lang.psi.NapileClass; import org.napile.compiler.lang.psi.NapileClassLike; import org.napile.compiler.lang.psi.NapileDeclaration; import org.napile.compiler.lang.psi.NapileModifierList; import org.napile.compiler.lang.psi.NapileModifierListOwner; import org.napile.compiler.lang.psi.NapileNamedDeclaration; import org.napile.compiler.lang.psi.NapileVariable; import org.napile.compiler.lang.resolve.BindingTraceUtil; import org.napile.compiler.lang.resolve.BindingTrace; import org.napile.compiler.lang.resolve.OverridingUtil; import org.napile.compiler.lang.resolve.TopDownAnalysisContext; import org.napile.compiler.lang.resolve.TopDownAnalysisParameters; import org.napile.compiler.lang.resolve.scopes.NapileScope; import org.napile.compiler.lang.types.NapileType; import org.napile.compiler.lang.types.checker.NapileTypeChecker; import org.napile.compiler.util.CommonSuppliers; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiElement; import com.intellij.util.containers.LinkedMultiMap; import com.intellij.util.containers.MultiMap; /** * @author abreslav */ public class OverrideResolver { private static final Logger LOGGER = Logger.getInstance(OverrideResolver.class); private TopDownAnalysisContext context; private TopDownAnalysisParameters topDownAnalysisParameters; private BindingTrace trace; @Inject public void setContext(TopDownAnalysisContext context) { this.context = context; } @Inject public void setTopDownAnalysisParameters(TopDownAnalysisParameters topDownAnalysisParameters) { this.topDownAnalysisParameters = topDownAnalysisParameters; } @Inject public void setTrace(BindingTrace trace) { this.trace = trace; } public void process() { //all created fake descriptors are stored to resolve visibility on them later generateOverrides(); checkVisibility(); checkOverrides(); checkParameterOverridesForAllClasses(); } /** * Generate fake overrides and add overridden descriptors to existing descriptors. */ private void generateOverrides() { Set<MutableClassDescriptor> ourClasses = new HashSet<MutableClassDescriptor>(); ourClasses.addAll(context.getClasses().values()); //ourClasses.addAll(context.getAnonymous().values()); ourClasses.addAll(context.getEnumValues().values()); Set<ClassifierDescriptor> processed = new HashSet<ClassifierDescriptor>(); for (MutableClassDescriptor clazz : ourClasses) { generateOverridesInAClass(clazz, processed, ourClasses); } } private void generateOverridesInAClass(@NotNull final MutableClassDescriptor classDescriptor, @NotNull Set<ClassifierDescriptor> processed, @NotNull Set<MutableClassDescriptor> ourClasses) { if (!processed.add(classDescriptor)) { return; } // avoid processing stdlib classes twice if (!ourClasses.contains(classDescriptor)) { return; } for (NapileType supertype : classDescriptor.getTypeConstructor().getSupertypes()) { ClassDescriptor superclass = (ClassDescriptor) supertype.getConstructor().getDeclarationDescriptor(); if (superclass instanceof MutableClassDescriptor) { generateOverridesInAClass((MutableClassDescriptor) superclass, processed, ourClasses); } } doGenerateOverridesInAClass(classDescriptor); } public void doGenerateOverridesInAClass(final MutableClassDescriptor classDescriptor) { List<CallableMemberDescriptor> membersFromSupertypes = getCallableMembersFromSupertypes(classDescriptor); MultiMap<Name, CallableMemberDescriptor> membersFromSupertypesByName = groupDescriptorsByName( membersFromSupertypes); MultiMap<Name, CallableMemberDescriptor> membersFromCurrentByName = groupDescriptorsByName( classDescriptor.getDeclaredCallableMembers()); Set<Name> memberNames = new LinkedHashSet<Name>(); memberNames.addAll(membersFromSupertypesByName.keySet()); memberNames.addAll(membersFromCurrentByName.keySet()); for (Name memberName : memberNames) { Collection<CallableMemberDescriptor> fromSupertypes = membersFromSupertypesByName.get(memberName); Collection<CallableMemberDescriptor> fromCurrent = membersFromCurrentByName.get(memberName); generateOverridesInFunctionGroup(memberName, fromSupertypes, fromCurrent, classDescriptor, new DescriptorSink() { @Override public void addToScope(@NotNull CallableMemberDescriptor fakeOverride) { if (fakeOverride instanceof VariableDescriptorImpl) { classDescriptor.getBuilder() .addVariableDescriptor((VariableDescriptorImpl) fakeOverride); } else if (fakeOverride instanceof MethodDescriptor) { classDescriptor.getBuilder().addMethodDescriptor((MethodDescriptor) fakeOverride); } else { throw new IllegalStateException(fakeOverride.getClass().getName()); } } @Override public void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent) { NapileDeclaration declaration = (NapileDeclaration) BindingTraceUtil .descriptorToDeclaration(trace, fromCurrent); trace.report(Errors.CONFLICTING_OVERLOADS.on(declaration, fromCurrent, fromCurrent.getContainingDeclaration().getName().getName())); } }); } } public interface DescriptorSink { void addToScope(@NotNull CallableMemberDescriptor fakeOverride); void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent); } public static void generateOverridesInFunctionGroup(@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) { List<CallableMemberDescriptor> notOverridden = Lists.newArrayList(membersFromSupertypes); for (CallableMemberDescriptor fromCurrent : membersFromCurrent) { extractAndBindOverridesForMember(fromCurrent, notOverridden, current, sink); } bindFakeOverrides(current, notOverridden, sink); } private static void extractAndBindOverridesForMember(@NotNull CallableMemberDescriptor fromCurrent, @NotNull List<CallableMemberDescriptor> notOverridden, @NotNull ClassDescriptor current, @NotNull DescriptorSink sink) { for (Iterator<CallableMemberDescriptor> iterator = notOverridden.iterator(); iterator.hasNext();) { CallableMemberDescriptor fromSupertype = iterator.next(); OverridingUtil.OverrideCompatibilityInfo.Result result = OverridingUtil .isOverridableBy(fromSupertype, fromCurrent).getResult(); boolean isVisible = Visibilities.isVisible(fromSupertype, current); switch (result) { case OVERRIDABLE: if (isVisible) { OverridingUtil.bindOverride(fromCurrent, fromSupertype); } iterator.remove(); break; case CONFLICT: if (isVisible) { sink.conflict(fromSupertype, fromCurrent); } iterator.remove(); break; case INCOMPATIBLE: break; } } } private static void bindFakeOverrides(@NotNull ClassDescriptor current, @NotNull List<CallableMemberDescriptor> notOverridden, @NotNull DescriptorSink sink) { Queue<CallableMemberDescriptor> fromSuperQueue = new LinkedList<CallableMemberDescriptor>(notOverridden); while (!fromSuperQueue.isEmpty()) { CallableMemberDescriptor notOverriddenFromSuper = fromSuperQueue.remove(); Collection<CallableMemberDescriptor> overridables = extractMembersOverridableBy(notOverriddenFromSuper, fromSuperQueue, sink); bindFakeOverride(notOverriddenFromSuper, overridables, current, sink); } } private static void bindFakeOverride(@NotNull CallableMemberDescriptor notOverriddenFromSuper, @NotNull Collection<CallableMemberDescriptor> overridables, @NotNull ClassDescriptor current, @NotNull DescriptorSink sink) { Collection<CallableMemberDescriptor> visibleOverridables = filterVisible(current, overridables); Modality modality = getMinimalModality(visibleOverridables); boolean allInvisible = visibleOverridables.isEmpty(); Collection<CallableMemberDescriptor> effectiveOverridden = allInvisible ? overridables : visibleOverridables; CallableMemberDescriptor fakeOverride = notOverriddenFromSuper.copy(current, modality, allInvisible, CallableMemberDescriptor.Kind.FAKE_OVERRIDE, false); for (CallableMemberDescriptor descriptor : effectiveOverridden) { OverridingUtil.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> filterVisible(@NotNull final ClassDescriptor current, @NotNull Collection<CallableMemberDescriptor> toFilter) { return Collections2.filter(toFilter, new Predicate<CallableMemberDescriptor>() { @Override public boolean apply(@Nullable CallableMemberDescriptor descriptor) { return Visibilities.isVisible(descriptor, current); } }); } @NotNull private static Collection<CallableMemberDescriptor> extractMembersOverridableBy( @NotNull CallableMemberDescriptor overrider, @NotNull Queue<CallableMemberDescriptor> extractFrom, @NotNull DescriptorSink sink) { Collection<CallableMemberDescriptor> overridable = Lists.newArrayList(); overridable.add(overrider); for (Iterator<CallableMemberDescriptor> iterator = extractFrom.iterator(); iterator.hasNext();) { CallableMemberDescriptor candidate = iterator.next(); OverridingUtil.OverrideCompatibilityInfo.Result result = OverridingUtil .isOverridableBy(candidate, overrider).getResult(); switch (result) { case OVERRIDABLE: overridable.add(candidate); iterator.remove(); break; case CONFLICT: sink.conflict(overrider, candidate); iterator.remove(); break; case INCOMPATIBLE: break; } } return overridable; } private static <T extends DeclarationDescriptor> MultiMap<Name, T> groupDescriptorsByName( Collection<T> properties) { MultiMap<Name, T> r = new LinkedMultiMap<Name, T>(); for (T property : properties) { r.putValue(property.getName(), property); } return r; } private static List<CallableMemberDescriptor> getCallableMembersFromSupertypes( ClassDescriptor classDescriptor) { Set<CallableMemberDescriptor> r = Sets.newLinkedHashSet(); for (NapileType supertype : classDescriptor.getTypeConstructor().getSupertypes()) { r.addAll(getCallableMembersFromType(supertype.getMemberScope())); } return new ArrayList<CallableMemberDescriptor>(r); } private static List<CallableMemberDescriptor> getCallableMembersFromType(NapileScope scope) { List<CallableMemberDescriptor> r = Lists.newArrayList(); for (DeclarationDescriptor decl : scope.getAllDescriptors()) { if (decl instanceof VariableDescriptor || decl instanceof MethodDescriptor) { r.add((CallableMemberDescriptor) decl); } } return r; } private void checkOverrides() { for (Map.Entry<NapileClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) checkOverridesInAClass(entry.getValue(), entry.getKey()); //for(Map.Entry<NapileAnonymClass, MutableClassDescriptor> entry : context.getAnonymous().entrySet()) // checkOverridesInAClass(entry.getValue(), entry.getKey()); } protected void checkOverridesInAClass(@NotNull MutableClassDescriptor classDescriptor, @NotNull NapileClassLike klass) { // Check overrides for internal consistency for (CallableMemberDescriptor member : classDescriptor.getDeclaredCallableMembers()) { checkOverrideForMember(member); } // Check if everything that must be overridden, actually is // More than one implementation or no implementations at all Set<CallableMemberDescriptor> abstractNoImpl = Sets.newLinkedHashSet(); Set<CallableMemberDescriptor> manyImpl = Sets.newLinkedHashSet(); collectMissingImplementations(classDescriptor, abstractNoImpl, manyImpl); PsiElement nameIdentifier = null; if (klass instanceof NapileClass) { nameIdentifier = klass.getNameIdentifier(); } else if (klass instanceof NapileAnonymClass) { nameIdentifier = klass.getNameIdentifier(); if (nameIdentifier == null) { nameIdentifier = ((NapileAnonymClass) klass).getObjectKeyword(); } } if (nameIdentifier == null) return; for (CallableMemberDescriptor memberDescriptor : manyImpl) { trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(nameIdentifier, klass, memberDescriptor)); break; } if (classDescriptor.getModality() == Modality.ABSTRACT) { return; } for (CallableMemberDescriptor memberDescriptor : abstractNoImpl) { trace.report(ABSTRACT_MEMBER_NOT_IMPLEMENTED.on(nameIdentifier, klass, memberDescriptor)); break; } } public static void collectMissingImplementations(MutableClassDescriptor classDescriptor, Set<CallableMemberDescriptor> abstractNoImpl, Set<CallableMemberDescriptor> manyImpl) { for (CallableMemberDescriptor descriptor : classDescriptor.getAllCallableMembers()) { collectMissingImplementations(descriptor, abstractNoImpl, manyImpl); } } private static void collectMissingImplementations(@NotNull CallableMemberDescriptor descriptor, @NotNull Set<CallableMemberDescriptor> abstractNoImpl, @NotNull Set<CallableMemberDescriptor> manyImpl) { if (descriptor.getKind().isReal()) return; Collection<CallableMemberDescriptor> overriddenDeclarations = OverridingUtil .getOverriddenDeclarations(descriptor); if (overriddenDeclarations.size() == 0) { throw new IllegalStateException("A 'fake override' must override something"); } List<CallableMemberDescriptor> nonAbstractManyImpl = Lists.newArrayList(); Set<CallableMemberDescriptor> filteredOverriddenDeclarations = OverridingUtil .filterOverrides(Sets.newHashSet(overriddenDeclarations)); boolean allSuperAbstract = true; for (CallableMemberDescriptor overridden : filteredOverriddenDeclarations) { if (overridden.getModality() != Modality.ABSTRACT) { if (descriptor.getVisibility() != Visibility.INVISIBLE_FAKE) { nonAbstractManyImpl.add(overridden); } allSuperAbstract = false; } } if (nonAbstractManyImpl.size() > 1) { manyImpl.addAll(nonAbstractManyImpl); } else if (allSuperAbstract) { abstractNoImpl.addAll(overriddenDeclarations); } } public static Multimap<CallableMemberDescriptor, CallableMemberDescriptor> collectSuperMethods( MutableClassDescriptor classDescriptor) { Set<CallableMemberDescriptor> inheritedFunctions = Sets.newLinkedHashSet(); for (NapileType supertype : classDescriptor.getSupertypes()) { for (DeclarationDescriptor descriptor : supertype.getMemberScope().getAllDescriptors()) { if (descriptor instanceof CallableMemberDescriptor) { CallableMemberDescriptor memberDescriptor = (CallableMemberDescriptor) descriptor; inheritedFunctions.add(memberDescriptor); } } } // Only those actually inherited Set<CallableMemberDescriptor> filteredMembers = OverridingUtil.filterOverrides(inheritedFunctions); // Group members with "the same" signature Multimap<CallableMemberDescriptor, CallableMemberDescriptor> factoredMembers = CommonSuppliers .newLinkedHashSetHashSetMultimap(); for (CallableMemberDescriptor one : filteredMembers) { if (factoredMembers.values().contains(one)) continue; for (CallableMemberDescriptor another : filteredMembers) { // if (one == another) continue; factoredMembers.put(one, one); if (OverridingUtil.isOverridableBy(one, another) .getResult() == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE || OverridingUtil.isOverridableBy(another, one) .getResult() == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE) { factoredMembers.put(one, another); } } } return factoredMembers; } private void checkOverrideForMember(@NotNull CallableMemberDescriptor declared) { NapileNamedDeclaration member = (NapileNamedDeclaration) BindingTraceUtil.descriptorToDeclaration(trace, declared); if (member == null) { if (declared.getKind() == CallableMemberDescriptor.Kind.CREATED_BY_PLUGIN) { return; } else if (declared.getKind() != CallableMemberDescriptor.Kind.DELEGATION) { throw new IllegalStateException("decriptor is not resolved to declaration" + " and it is not delegate: " + declared + ", DELEGATED: " + (declared.getKind() == CallableMemberDescriptor.Kind.DELEGATION)); } return; } if (declared.getKind() != CallableMemberDescriptor.Kind.DECLARATION) { return; } NapileModifierList modifierList = member.getModifierList(); ASTNode overrideNode = modifierList != null ? modifierList.getModifierNode(NapileTokens.OVERRIDE_KEYWORD) : null; boolean hasOverrideModifier = overrideNode != null; boolean finalOverriddenError = false; boolean typeMismatchError = false; boolean kindMismatchError = false; for (CallableMemberDescriptor overridden : declared.getOverriddenDescriptors()) { if (overridden != null) { if (hasOverrideModifier) { if (!overridden.getModality().isOverridable() && !finalOverriddenError) { trace.report(OVERRIDING_FINAL_MEMBER.on(overrideNode.getPsi(), overridden, overridden.getContainingDeclaration())); finalOverriddenError = true; } if (!OverridingUtil.isReturnTypeOkForOverride(NapileTypeChecker.INSTANCE, overridden, declared) && !typeMismatchError) { trace.report(RETURN_TYPE_MISMATCH_ON_OVERRIDE.on(member, declared, overridden)); typeMismatchError = true; } if (!isVal(overridden) && isVal(declared) && !kindMismatchError) { trace.report(VAR_OVERRIDDEN_BY_VAL.on((NapileVariable) member, (VariableDescriptorImpl) declared, (VariableDescriptorImpl) overridden)); kindMismatchError = true; } } } } if (hasOverrideModifier && declared.getOverriddenDescriptors().size() == 0) { DeclarationDescriptor containingDeclaration = declared.getContainingDeclaration(); assert containingDeclaration instanceof ClassDescriptor : "Overrides may only be resolved in a class, but " + declared + " comes from " + containingDeclaration; ClassDescriptor declaringClass = (ClassDescriptor) containingDeclaration; CallableMemberDescriptor invisibleOverriddenDescriptor = findInvisibleOverriddenDescriptor(declared, declaringClass); if (invisibleOverriddenDescriptor != null) { trace.report(CANNOT_OVERRIDE_INVISIBLE_MEMBER.on(member, declared, invisibleOverriddenDescriptor, invisibleOverriddenDescriptor.getContainingDeclaration())); } else { trace.report(NOTHING_TO_OVERRIDE.on(member, declared)); } } PsiElement nameIdentifier = member.getNameIdentifier(); if (!hasOverrideModifier && declared.getOverriddenDescriptors().size() > 0 && nameIdentifier != null) { CallableMemberDescriptor overridden = declared.getOverriddenDescriptors().iterator().next(); trace.report( VIRTUAL_MEMBER_HIDDEN.on(member, declared, overridden, overridden.getContainingDeclaration())); } } private CallableMemberDescriptor findInvisibleOverriddenDescriptor(CallableMemberDescriptor declared, ClassDescriptor declaringClass) { CallableMemberDescriptor invisibleOverride = null; outer: for (NapileType supertype : declaringClass.getTypeConstructor().getSupertypes()) { Set<CallableMemberDescriptor> all = Sets.newLinkedHashSet(); all.addAll(supertype.getMemberScope().getMethods(declared.getName())); all.addAll((Set) supertype.getMemberScope().getVariables(declared.getName())); for (CallableMemberDescriptor fromSuper : all) { if (OverridingUtil.isOverridableBy(fromSuper, declared) .getResult() == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE) { invisibleOverride = fromSuper; if (Visibilities.isVisible(fromSuper, declared)) { LOGGER.error("Descriptor " + fromSuper + "is overridable by " + declared + " and visible but does not appear in its getOverriddenDescriptors()"); } break outer; } } } return invisibleOverride; } private void checkParameterOverridesForAllClasses() { List<MutableClassDescriptor> allClasses = Lists.newArrayList(context.getClasses().values()); //allClasses.addAll(context.getAnonymous().values()); for (MutableClassDescriptor classDescriptor : allClasses) { Collection<CallableMemberDescriptor> members = classDescriptor.getAllCallableMembers(); for (CallableMemberDescriptor member : members) { checkOverridesForParameters(member); } } } public void checkOverridesForParameters(CallableMemberDescriptor declared) { if (declared.getKind() == CallableMemberDescriptor.Kind.CREATED_BY_PLUGIN) { return; } boolean fakeOverride = declared.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE; if (!fakeOverride) { // No check if the function is not marked as 'override' NapileModifierListOwner declaration = (NapileModifierListOwner) BindingTraceUtil .descriptorToDeclaration(trace, declared); if (!declaration.hasModifier(NapileTokens.OVERRIDE_KEYWORD)) { return; } } // Let p1 be a parameter of the overriding function // Let p2 be a parameter of the function being overridden // Then // a) p1 is not allowed to have a default value declared // b) p1 must have the same name as p2 for (CallParameterDescriptor parameterFromSubclass : declared.getValueParameters()) { NapileCallParameterAsVariable parameter = fakeOverride ? null : (NapileCallParameterAsVariable) BindingTraceUtil.descriptorToDeclaration(trace, parameterFromSubclass); NapileClassLike classElement = fakeOverride ? (NapileClassLike) BindingTraceUtil.descriptorToDeclaration(trace, declared.getContainingDeclaration()) : null; if (parameterFromSubclass.declaresDefaultValue() && !fakeOverride) { trace.report(DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE.on(parameter)); } boolean superWithDefault = false; for (CallParameterDescriptor parameterFromSuperclass : parameterFromSubclass .getOverriddenDescriptors()) { if (parameterFromSuperclass.declaresDefaultValue()) { if (!superWithDefault) { superWithDefault = true; } else { if (fakeOverride) { trace.report(MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE .on(classElement, parameterFromSubclass)); } else { trace.report(MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES.on(parameter, parameterFromSubclass)); } break; } } if (!parameterFromSuperclass.getName().equals(parameterFromSubclass.getName())) { if (fakeOverride) { trace.report(DIFFERENT_NAMES_FOR_THE_SAME_PARAMETER_IN_SUPERTYPES.on(classElement, declared.getOverriddenDescriptors(), parameterFromSuperclass.getIndex() + 1)); } else { trace.report( PARAMETER_NAME_CHANGED_ON_OVERRIDE.on( parameter, (ClassDescriptor) parameterFromSuperclass .getContainingDeclaration().getContainingDeclaration(), parameterFromSuperclass)); } } } } } private static boolean isVal(CallableMemberDescriptor descriptor) { return descriptor instanceof VariableDescriptor && !((VariableDescriptor) descriptor).isMutable(); } @Nullable private static Visibility findMaxVisibility( @NotNull Collection<? extends CallableMemberDescriptor> descriptors) { if (descriptors.isEmpty()) { return Visibility.PUBLIC; } Visibility maxVisibility = null; for (CallableMemberDescriptor descriptor : descriptors) { Visibility visibility = descriptor.getVisibility(); 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; } private void checkVisibility() { for (Map.Entry<NapileDeclaration, CallableMemberDescriptor> entry : context.getMembers().entrySet()) { checkVisibilityForMember(entry.getKey(), entry.getValue()); } } private void checkVisibilityForMember(@NotNull NapileDeclaration declaration, @NotNull CallableMemberDescriptor memberDescriptor) { Visibility visibility = memberDescriptor.getVisibility(); for (CallableMemberDescriptor descriptor : memberDescriptor.getOverriddenDescriptors()) { Integer compare = Visibilities.compare(visibility, descriptor.getVisibility()); if (compare == null) { trace.report(CANNOT_CHANGE_ACCESS_PRIVILEGE.on(declaration, descriptor.getVisibility(), descriptor, descriptor.getContainingDeclaration())); return; } else if (compare < 0) { trace.report(CANNOT_WEAKEN_ACCESS_PRIVILEGE.on(declaration, descriptor.getVisibility(), descriptor, descriptor.getContainingDeclaration())); return; } } } }