org.napile.compiler.lang.resolve.processors.OverrideResolver.java Source code

Java tutorial

Introduction

Here is the source code for org.napile.compiler.lang.resolve.processors.OverrideResolver.java

Source

/*
 * 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;
            }
        }
    }
}