All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.resolve.OverrideResolver Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2015 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.jetbrains.kotlin.resolve;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.psi.PsiElement;
import com.intellij.util.Function;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.LinkedMultiMap;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.hash.EqualityPolicy;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.CallResolverUtilKt;
import org.jetbrains.kotlin.resolve.dataClassUtils.DataClassUtilsKt;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.types.*;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import org.jetbrains.kotlin.utils.HashSetUtil;

import java.util.*;

import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.*;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveAbstractMembers;
import static org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE;

public class OverrideResolver {

    @NotNull private final BindingTrace trace;

    public OverrideResolver(@NotNull BindingTrace trace) {
        this.trace = trace;
    }

    public void check(@NotNull TopDownAnalysisContext c) {
        checkVisibility(c);
        checkOverrides(c);
        checkParameterOverridesForAllClasses(c);
    }

    public static void generateOverridesInAClass(
            @NotNull ClassDescriptor classDescriptor,
            @NotNull Collection membersFromCurrent,
            @NotNull OverridingUtil.DescriptorSink sink
    ) {
        List membersFromSupertypes = getCallableMembersFromSupertypes(classDescriptor);
        MultiMap membersFromCurrentByName = groupDescriptorsByName(membersFromCurrent);
        MultiMap membersFromSupertypesByName = groupDescriptorsByName(membersFromSupertypes);

        Set memberNames = new LinkedHashSet();
        memberNames.addAll(membersFromSupertypesByName.keySet());
        memberNames.addAll(membersFromCurrentByName.keySet());

        for (Name memberName : memberNames) {
            Collection fromSupertypes = membersFromSupertypesByName.get(memberName);
            Collection fromCurrent = membersFromCurrentByName.get(memberName);

            OverridingUtil.generateOverridesInFunctionGroup(memberName, fromSupertypes, fromCurrent, classDescriptor, sink);
        }
    }

    public static void resolveUnknownVisibilities(
            @NotNull Collection descriptors,
            @NotNull BindingTrace trace
    ) {
        for (CallableMemberDescriptor descriptor : descriptors) {
            OverridingUtil.resolveUnknownVisibilityForMember(descriptor, createCannotInferVisibilityReporter(trace));
        }
    }

    @NotNull
    public static Function1 createCannotInferVisibilityReporter(@NotNull final BindingTrace trace) {
        return new Function1() {
            @Override
            public Unit invoke(@NotNull CallableMemberDescriptor descriptor) {
                DeclarationDescriptor reportOn;
                if (descriptor.getKind() == FAKE_OVERRIDE || descriptor.getKind() == DELEGATION) {
                    reportOn = DescriptorUtils.getParentOfType(descriptor, ClassDescriptor.class);
                }
                else if (descriptor instanceof PropertyAccessorDescriptor && ((PropertyAccessorDescriptor) descriptor).isDefault()) {
                    reportOn = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty();
                }
                else {
                    reportOn = descriptor;
                }
                //noinspection ConstantConditions
                PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(reportOn);
                if (element instanceof KtDeclaration) {
                    trace.report(CANNOT_INFER_VISIBILITY.on((KtDeclaration) element, descriptor));
                }
                return Unit.INSTANCE$;
            }
        };
    }

    private static enum Filtering {
        RETAIN_OVERRIDING,
        RETAIN_OVERRIDDEN
    }

    @NotNull
    public static  Set filterOutOverridden(@NotNull Set candidateSet) {
        //noinspection unchecked
        return filterOverrides(candidateSet, Function.ID, Filtering.RETAIN_OVERRIDING);
    }

    @NotNull
    public static  Set filterOutOverriding(@NotNull Set candidateSet) {
        //noinspection unchecked
        return filterOverrides(candidateSet, Function.ID, Filtering.RETAIN_OVERRIDDEN);
    }

    @NotNull
    public static  Set filterOutOverridden(
            @NotNull Set candidateSet,
            @NotNull Function transform
    ) {
        return filterOverrides(candidateSet, transform, Filtering.RETAIN_OVERRIDING);
    }

    @NotNull
    private static  Set filterOverrides(
            @NotNull Set candidateSet,
            @NotNull final Function transform,
            @NotNull Filtering filtering
    ) {
        if (candidateSet.size() <= 1) return candidateSet;

        // 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).
        // Here we filter out structurally equivalent descriptors before processing overrides, because such descriptors
        // "override" each other (overrides(f, g) = overrides(g, f) = true) and the code below removes them all from the
        // candidates, unless we first compute noDuplicates
        Set noDuplicates = HashSetUtil.linkedHashSet(
                candidateSet,
                new EqualityPolicy() {
                    @Override
                    public int getHashCode(D d) {
                        return DescriptorUtils.getFqName(transform.fun(d).getContainingDeclaration()).hashCode();
                    }

                    @Override
                    public boolean isEqual(D d1, D d2) {
                        CallableDescriptor f = transform.fun(d1);
                        CallableDescriptor g = transform.fun(d2);
                        return DescriptorEquivalenceForOverrides.INSTANCE$.areEquivalent(f.getOriginal(), g.getOriginal());
                    }
                });

        Set candidates = Sets.newLinkedHashSet();
        outerLoop:
        for (D meD : noDuplicates) {
            CallableDescriptor me = transform.fun(meD);
            for (D otherD : noDuplicates) {
                CallableDescriptor other = transform.fun(otherD);
                if (me == other) continue;
                if (filtering == Filtering.RETAIN_OVERRIDING) {
                    if (overrides(other, me)) {
                        continue outerLoop;
                    }
                }
                else if (filtering == Filtering.RETAIN_OVERRIDDEN) {
                    if (overrides(me, other)) {
                        continue outerLoop;
                    }
                }
                else {
                    throw new AssertionError("Unexpected Filtering object: " + filtering);
                }
            }
            for (D otherD : candidates) {
                CallableDescriptor other = transform.fun(otherD);
                if (me.getOriginal() == other.getOriginal()
                    && OverridingUtil.DEFAULT.isOverridableBy(other, me).getResult() == OVERRIDABLE
                    && OverridingUtil.DEFAULT.isOverridableBy(me, other).getResult() == OVERRIDABLE) {
                    continue outerLoop;
                }
            }
            candidates.add(meD);
        }

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

        return candidates;
    }

    // check whether f overrides g
    public static  boolean overrides(@NotNull D f, @NotNull D g) {
        // 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
        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.getOriginal())) return true;
        }
        return false;
    }

    private static  MultiMap groupDescriptorsByName(Collection properties) {
        MultiMap r = new LinkedMultiMap();
        for (T property : properties) {
            r.putValue(property.getName(), property);
        }
        return r;
    }


    private static List getCallableMembersFromSupertypes(ClassDescriptor classDescriptor) {
        Set r = Sets.newLinkedHashSet();
        for (KotlinType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
            r.addAll(getCallableMembersFromType(supertype));
        }
        return new ArrayList(r);
    }

    private static List getCallableMembersFromType(KotlinType type) {
        List r = Lists.newArrayList();
        for (DeclarationDescriptor decl : type.getMemberScope().getAllDescriptors()) {
            if (decl instanceof PropertyDescriptor || decl instanceof SimpleFunctionDescriptor) {
                r.add((CallableMemberDescriptor) decl);
            }
        }
        return r;
    }

    private void checkOverrides(@NotNull TopDownAnalysisContext c) {
        for (Map.Entry entry : c.getDeclaredClasses().entrySet()) {
            checkOverridesInAClass(entry.getValue(), entry.getKey());
        }
    }

    private void checkOverridesInAClass(@NotNull ClassDescriptorWithResolutionScopes classDescriptor, @NotNull KtClassOrObject 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 abstractNoImpl = Sets.newLinkedHashSet();
        Set manyImpl = Sets.newLinkedHashSet();
        Set abstractInBaseClassNoImpl = Sets.newLinkedHashSet();
        Set conflictingInterfaceOverrides = Sets.newLinkedHashSet();
        collectMissingImplementations(classDescriptor,
                                      abstractNoImpl, manyImpl,
                                      abstractInBaseClassNoImpl, conflictingInterfaceOverrides);

        if (!classCanHaveAbstractMembers(classDescriptor)) {
            if (!abstractInBaseClassNoImpl.isEmpty()) {
                trace.report(ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.on(klass, klass, abstractInBaseClassNoImpl.iterator().next()));
            }
            else if (!abstractNoImpl.isEmpty()) {
                trace.report(ABSTRACT_MEMBER_NOT_IMPLEMENTED.on(klass, klass, abstractNoImpl.iterator().next()));
            }
        }

        if (!conflictingInterfaceOverrides.isEmpty()) {
            trace.report(MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.on(klass, klass, conflictingInterfaceOverrides.iterator().next()));
        }
        else if (!manyImpl.isEmpty()) {
            trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(klass, klass, manyImpl.iterator().next()));
        }
    }

    @NotNull
    public static Set getMissingImplementations(@NotNull ClassDescriptor classDescriptor) {
        Set shouldImplement = new LinkedHashSet();
        Set dontCare = new HashSet();
        collectMissingImplementations(classDescriptor, shouldImplement, shouldImplement, dontCare, dontCare);
        return shouldImplement;
    }

    private static void collectMissingImplementations(
            @NotNull ClassDescriptor classDescriptor,
            @NotNull Set abstractNoImpl,
            @NotNull Set manyImpl,
            @NotNull Set abstractInBaseClassNoImpl,
            @NotNull Set conflictingInterfaceOverrides
    ) {
        for (DeclarationDescriptor member : classDescriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
            if (member instanceof CallableMemberDescriptor) {
                collectMissingImplementations((CallableMemberDescriptor) member,
                                              abstractNoImpl, manyImpl,
                                              abstractInBaseClassNoImpl, conflictingInterfaceOverrides);
            }
        }
    }

    private static void collectMissingImplementations(
            @NotNull CallableMemberDescriptor descriptor,
            @NotNull Set abstractNoImpl,
            @NotNull Set manyImpl,
            @NotNull Set abstractInBaseClassNoImpl,
            @NotNull Set conflictingInterfaceOverrides
    ) {
        if (descriptor.getKind().isReal()) return;
        if (descriptor.getVisibility() == Visibilities.INVISIBLE_FAKE) return;

        Collection directOverridden = descriptor.getOverriddenDescriptors();
        if (directOverridden.size() == 0) {
            throw new IllegalStateException("A 'fake override' " + descriptor.getName().asString() + " must override something");
        }

        // collects map from the directly overridden descriptor to the set of declarations:
        // -- if directly overridden is not fake, the set consists of one element: this directly overridden
        // -- if it's fake, overridden declarations (non-fake) of this descriptor are collected
        Map> overriddenDeclarationsByDirectParent = collectOverriddenDeclarations(directOverridden);

        List allOverriddenDeclarations = ContainerUtil.flatten(overriddenDeclarationsByDirectParent.values());
        Set allFilteredOverriddenDeclarations = filterOutOverridden(
                Sets.newLinkedHashSet(allOverriddenDeclarations));

        Set relevantDirectlyOverridden =
                getRelevantDirectlyOverridden(overriddenDeclarationsByDirectParent, allFilteredOverriddenDeclarations);

        collectJava8MissingOverrides(relevantDirectlyOverridden, abstractInBaseClassNoImpl, conflictingInterfaceOverrides);

        List implementations = collectImplementations(relevantDirectlyOverridden);
        if (implementations.size() == 1 && isReturnTypeOkForOverride(descriptor, implementations.get(0))) return;

        List abstractOverridden = new ArrayList(allFilteredOverriddenDeclarations.size());
        List concreteOverridden = new ArrayList(allFilteredOverriddenDeclarations.size());
        filterNotSynthesizedDescriptorsByModality(allFilteredOverriddenDeclarations, abstractOverridden, concreteOverridden);

        if (implementations.isEmpty()) {
            abstractNoImpl.addAll(abstractOverridden);
        }
        else if (implementations.size() > 1) {
            manyImpl.addAll(concreteOverridden);
        }
        else {
            abstractNoImpl.addAll(collectAbstractMethodsWithMoreSpecificReturnType(abstractOverridden, implementations.get(0)));
        }
    }

    private static void collectJava8MissingOverrides(
            Set relevantDirectlyOverridden,
            @NotNull Set abstractInBaseClassNoImpl,
            @NotNull Set conflictingInterfaceOverrides
    ) {
        // Java 8:
        // -- class should implement an abstract member of a super-class,
        //    even if relevant default implementation is provided in one of the super-interfaces;
        // -- inheriting multiple override equivalent methods from an interface is a conflict
        //    regardless of 'default' vs 'abstract'.

        boolean overridesClassMember = false;
        boolean overridesNonAbstractInterfaceMember = false;
        CallableMemberDescriptor overridesAbstractInBaseClass = null;
        List overriddenInterfaceMembers = new SmartList();
        for (CallableMemberDescriptor overridden : relevantDirectlyOverridden) {
            DeclarationDescriptor containingDeclaration = overridden.getContainingDeclaration();
            if (containingDeclaration instanceof ClassDescriptor) {
                ClassDescriptor baseClassOrInterface = (ClassDescriptor) containingDeclaration;
                if (baseClassOrInterface.getKind() == ClassKind.CLASS) {
                    overridesClassMember = true;
                    if (overridden.getModality() == Modality.ABSTRACT) {
                        overridesAbstractInBaseClass = overridden;
                    }
                }
                else if (baseClassOrInterface.getKind() == ClassKind.INTERFACE) {
                    overriddenInterfaceMembers.add(overridden);
                    if (overridden.getModality() != Modality.ABSTRACT) {
                        overridesNonAbstractInterfaceMember = true;
                    }
                }
            }
        }

        if (overridesAbstractInBaseClass != null) {
            abstractInBaseClassNoImpl.add(overridesAbstractInBaseClass);
        }

        if (!overridesClassMember && overridesNonAbstractInterfaceMember && overriddenInterfaceMembers.size() > 1) {
            conflictingInterfaceOverrides.addAll(overriddenInterfaceMembers);
        }
    }

    @NotNull
    private static List collectImplementations(@NotNull Set relevantDirectlyOverridden) {
        List result = new ArrayList(relevantDirectlyOverridden.size());
        for (CallableMemberDescriptor overriddenDescriptor : relevantDirectlyOverridden) {
            if (overriddenDescriptor.getModality() != Modality.ABSTRACT) {
                result.add(overriddenDescriptor);
            }
        }
        return result;
    }

    private static void filterNotSynthesizedDescriptorsByModality(
            @NotNull Set allOverriddenDeclarations,
            @NotNull List abstractOverridden,
            @NotNull List concreteOverridden
    ) {
        for (CallableMemberDescriptor overridden : allOverriddenDeclarations) {
            if (!CallResolverUtilKt.isOrOverridesSynthesized(overridden)) {
                if (overridden.getModality() == Modality.ABSTRACT) {
                    abstractOverridden.add(overridden);
                }
                else {
                    concreteOverridden.add(overridden);
                }
            }
        }
    }

    @NotNull
    private static List collectAbstractMethodsWithMoreSpecificReturnType(
            @NotNull List abstractOverridden,
            @NotNull CallableMemberDescriptor implementation
    ) {
        List result = new ArrayList(abstractOverridden.size());
        for (CallableMemberDescriptor abstractMember : abstractOverridden) {
            if (!isReturnTypeOkForOverride(abstractMember, implementation)) {
                result.add(abstractMember);
            }
        }
        assert !result.isEmpty() : "Implementation (" + implementation + ") doesn't have the most specific type, " +
                                   "but none of the other overridden methods does either: " + abstractOverridden;
        return result;
    }

    @NotNull
    private static Set getRelevantDirectlyOverridden(
            @NotNull Map> overriddenByParent,
            @NotNull Set allFilteredOverriddenDeclarations
    ) {
        /* Let the following class hierarchy is declared:

        trait A { fun foo() = 1 }
        trait B : A
        trait C : A
        trait D : A { override fun foo() = 2 }
        trait E : B, C, D {}

        Traits B and C have fake descriptors for function foo.
        The map 'overriddenByParent' is:
        { 'foo in B' (fake) -> { 'foo in A' }, 'foo in C' (fake) -> { 'foo in A' }, 'foo in D' -> { 'foo in D'} }
        This is a map from directly overridden descriptors (functions 'foo' in B, C, D in this example) to the set of declarations (non-fake),
        that are overridden by this descriptor.

        The goal is to leave only relevant directly overridden descriptors to count implementations of our fake function on them.
        In the example above there is no error (trait E inherits only one implementation of 'foo' (from D), because this implementation is more precise).
        So only 'foo in D' is relevant.

        Directly overridden descriptor is not relevant if it doesn't add any more appropriate non-fake declarations of the concerned function.
        More precisely directly overridden descriptor is not relevant if:
        - it's declaration set is a subset of declaration set for other directly overridden descriptor
        ('foo in B' is not relevant because it's declaration set is a subset of 'foo in C' function's declaration set)
        - each member of it's declaration set is overridden by a member of other declaration set
        ('foo in C' is not relevant, because 'foo in A' is overridden by 'foo in D', so 'foo in A' is not appropriate non-fake declaration for 'foo')

        For the last condition allFilteredOverriddenDeclarations helps (for the example above it's { 'foo in A' } only): each declaration set
        is compared with allFilteredOverriddenDeclarations, if they have no intersection, this means declaration set has only functions that
        are overridden by some other function and corresponding directly overridden descriptor is not relevant.
        */

        for (Iterator>> iterator =
                     overriddenByParent.entrySet().iterator(); iterator.hasNext(); ) {
            if (!isRelevant(iterator.next().getValue(), overriddenByParent.values(), allFilteredOverriddenDeclarations)) {
                iterator.remove();
            }
        }
        return overriddenByParent.keySet();
    }

    private static boolean isRelevant(
            @NotNull Set declarationSet,
            @NotNull Collection> allDeclarationSets,
            @NotNull Set allFilteredOverriddenDeclarations
    ) {
        for (Set otherSet : allDeclarationSets) {
            if (otherSet == declarationSet) continue;
            if (otherSet.containsAll(declarationSet)) return false;
            if (Collections.disjoint(allFilteredOverriddenDeclarations, declarationSet)) return false;
        }
        return true;
    }

    @NotNull
    private static Map> collectOverriddenDeclarations(
            @NotNull Collection directOverriddenDescriptors
    ) {
        Map> overriddenDeclarationsByDirectParent = Maps.newLinkedHashMap();
        for (CallableMemberDescriptor descriptor : directOverriddenDescriptors) {
            Set overriddenDeclarations = getOverriddenDeclarations(descriptor);
            Set filteredOverrides = filterOutOverridden(overriddenDeclarations);
            overriddenDeclarationsByDirectParent.put(descriptor, new LinkedHashSet(filteredOverrides));
        }
        return overriddenDeclarationsByDirectParent;
    }

    /**
     * @return overridden real descriptors (not fake overrides). Note that all usages of this method should be followed by calling
     * {@link #filterOutOverridden(java.util.Set)} or {@link #filterOutOverriding(java.util.Set)}, because some of the declarations
     * can override the other
     * TODO: merge this method with filterOutOverridden
     */
    @NotNull
    public static Set getOverriddenDeclarations(@NotNull CallableMemberDescriptor descriptor) {
        Set result = new LinkedHashSet();
        getOverriddenDeclarations(descriptor, result);
        return result;
    }

    private static void getOverriddenDeclarations(
            @NotNull CallableMemberDescriptor descriptor,
            @NotNull Set 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()) {
                getOverriddenDeclarations(overridden, result);
            }
        }
    }

    private interface CheckOverrideReportStrategy {
        void overridingFinalMember(@NotNull CallableMemberDescriptor overridden);

        void returnTypeMismatchOnOverride(@NotNull CallableMemberDescriptor overridden);

        void propertyTypeMismatchOnOverride(@NotNull CallableMemberDescriptor overridden);

        void varOverriddenByVal(@NotNull CallableMemberDescriptor overridden);

        void cannotOverrideInvisibleMember(@NotNull CallableMemberDescriptor invisibleOverridden);

        void nothingToOverride();
    }

    private void checkOverrideForMember(@NotNull final CallableMemberDescriptor declared) {
        if (declared.getKind() == CallableMemberDescriptor.Kind.SYNTHESIZED) {
            if (DataClassUtilsKt.isComponentLike(declared.getName())) {
                checkOverrideForComponentFunction(declared);
            }
            return;
        }

        if (declared.getKind() != CallableMemberDescriptor.Kind.DECLARATION) {
            return;
        }

        final KtNamedDeclaration member = (KtNamedDeclaration) DescriptorToSourceUtils.descriptorToDeclaration(declared);
        if (member == null) {
            throw new IllegalStateException("declared descriptor is not resolved to declaration: " + declared);
        }

        KtModifierList modifierList = member.getModifierList();
        boolean hasOverrideNode = modifierList != null && modifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD);
        Collection overriddenDescriptors = declared.getOverriddenDescriptors();

        if (hasOverrideNode) {
            checkOverridesForMemberMarkedOverride(declared, true, new CheckOverrideReportStrategy() {
                private boolean finalOverriddenError = false;
                private boolean typeMismatchError = false;
                private boolean kindMismatchError = false;

                @Override
                public void overridingFinalMember(@NotNull CallableMemberDescriptor overridden) {
                    if (!finalOverriddenError) {
                        finalOverriddenError = true;
                        trace.report(OVERRIDING_FINAL_MEMBER.on(member, overridden, overridden.getContainingDeclaration()));
                    }
                }

                @Override
                public void returnTypeMismatchOnOverride(@NotNull CallableMemberDescriptor overridden) {
                    if (!typeMismatchError) {
                        typeMismatchError = true;
                        trace.report(RETURN_TYPE_MISMATCH_ON_OVERRIDE.on(member, declared, overridden));
                    }
                }

                @Override
                public void propertyTypeMismatchOnOverride(@NotNull CallableMemberDescriptor overridden) {
                    if (!typeMismatchError) {
                        typeMismatchError = true;
                        trace.report(PROPERTY_TYPE_MISMATCH_ON_OVERRIDE.on(member, declared, overridden));
                    }
                }

                @Override
                public void varOverriddenByVal(@NotNull CallableMemberDescriptor overridden) {
                    if (!kindMismatchError) {
                        kindMismatchError = true;
                        trace.report(VAR_OVERRIDDEN_BY_VAL.on(member, (PropertyDescriptor) declared, (PropertyDescriptor) overridden));
                    }
                }

                @Override
                public void cannotOverrideInvisibleMember(@NotNull CallableMemberDescriptor invisibleOverridden) {
                    trace.report(CANNOT_OVERRIDE_INVISIBLE_MEMBER.on(member, declared, invisibleOverridden));
                }

                @Override
                public void nothingToOverride() {
                    trace.report(NOTHING_TO_OVERRIDE.on(member, declared));
                }
            });
        }
        else if (!overriddenDescriptors.isEmpty()) {
            CallableMemberDescriptor overridden = overriddenDescriptors.iterator().next();
            trace.report(VIRTUAL_MEMBER_HIDDEN.on(member, declared, overridden, overridden.getContainingDeclaration()));
        }
    }

    private static void checkOverridesForMemberMarkedOverride(
            @NotNull CallableMemberDescriptor declared,
            boolean checkIfOverridesNothing,
            @NotNull CheckOverrideReportStrategy reportError
    ) {
        Collection overriddenDescriptors = declared.getOverriddenDescriptors();

        for (CallableMemberDescriptor overridden : overriddenDescriptors) {
            if (overridden == null) continue;

            if (!overridden.getModality().isOverridable()) {
                reportError.overridingFinalMember(overridden);
            }

            if (declared instanceof PropertyDescriptor &&
                !isPropertyTypeOkForOverride((PropertyDescriptor) overridden, (PropertyDescriptor) declared)) {
                reportError.propertyTypeMismatchOnOverride(overridden);
            }
            else if (!isReturnTypeOkForOverride(overridden, declared)) {
                reportError.returnTypeMismatchOnOverride(overridden);
            }

            if (checkPropertyKind(overridden, true) && checkPropertyKind(declared, false)) {
                reportError.varOverriddenByVal(overridden);
            }
        }

        if (checkIfOverridesNothing && overriddenDescriptors.isEmpty()) {
            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) {
                reportError.cannotOverrideInvisibleMember(invisibleOverriddenDescriptor);
            }
            else {
                reportError.nothingToOverride();
            }
        }
    }

    public static boolean isReturnTypeOkForOverride(
            @NotNull CallableDescriptor superDescriptor,
            @NotNull CallableDescriptor subDescriptor
    ) {
        TypeSubstitutor typeSubstitutor = prepareTypeSubstitutor(superDescriptor, subDescriptor);
        if (typeSubstitutor == null) return false;

        KotlinType superReturnType = superDescriptor.getReturnType();
        assert superReturnType != null;

        KotlinType subReturnType = subDescriptor.getReturnType();
        assert subReturnType != null;

        KotlinType substitutedSuperReturnType = typeSubstitutor.substitute(superReturnType, Variance.OUT_VARIANCE);
        assert substitutedSuperReturnType != null;

        return KotlinTypeChecker.DEFAULT.isSubtypeOf(subReturnType, substitutedSuperReturnType);
    }

    @Nullable
    private static TypeSubstitutor prepareTypeSubstitutor(
            @NotNull CallableDescriptor superDescriptor,
            @NotNull CallableDescriptor subDescriptor
    ) {
        List superTypeParameters = superDescriptor.getTypeParameters();
        List subTypeParameters = subDescriptor.getTypeParameters();
        if (subTypeParameters.size() != superTypeParameters.size()) return null;

        ArrayList arguments = new ArrayList(subTypeParameters.size());
        for (int i = 0; i < superTypeParameters.size(); i++) {
            arguments.add(new TypeProjectionImpl(subTypeParameters.get(i).getDefaultType()));
        }

        return new IndexedParametersSubstitution(superTypeParameters, arguments).buildSubstitutor();
    }

    public static boolean isPropertyTypeOkForOverride(
            @NotNull PropertyDescriptor superDescriptor,
            @NotNull PropertyDescriptor subDescriptor
    ) {
        TypeSubstitutor typeSubstitutor = prepareTypeSubstitutor(superDescriptor, subDescriptor);
        if (typeSubstitutor == null) return false;

        if (!superDescriptor.isVar()) return true;

        KotlinType substitutedSuperReturnType = typeSubstitutor.substitute(superDescriptor.getType(), Variance.OUT_VARIANCE);
        assert substitutedSuperReturnType != null;
        return KotlinTypeChecker.DEFAULT.equalTypes(subDescriptor.getType(), substitutedSuperReturnType);
    }

    private void checkOverrideForComponentFunction(@NotNull final CallableMemberDescriptor componentFunction) {
        final PsiElement dataModifier = findDataModifierForDataClass(componentFunction.getContainingDeclaration());

        checkOverridesForMemberMarkedOverride(componentFunction, false, new CheckOverrideReportStrategy() {
            private boolean overrideConflict = false;

            @Override
            public void overridingFinalMember(@NotNull CallableMemberDescriptor overridden) {
                if (!overrideConflict) {
                    overrideConflict = true;
                    trace.report(DATA_CLASS_OVERRIDE_CONFLICT.on(dataModifier, componentFunction, overridden.getContainingDeclaration()));
                }
            }

            @Override
            public void returnTypeMismatchOnOverride(@NotNull CallableMemberDescriptor overridden) {
                if (!overrideConflict) {
                    overrideConflict = true;
                    trace.report(DATA_CLASS_OVERRIDE_CONFLICT.on(dataModifier, componentFunction, overridden.getContainingDeclaration()));
                }
            }

            @Override
            public void propertyTypeMismatchOnOverride(@NotNull CallableMemberDescriptor overridden) {
                throw new IllegalStateException("Component functions are not properties");
            }

            @Override
            public void varOverriddenByVal(@NotNull CallableMemberDescriptor overridden) {
                throw new IllegalStateException("Component functions are not properties");
            }

            @Override
            public void cannotOverrideInvisibleMember(@NotNull CallableMemberDescriptor invisibleOverridden) {
                throw new IllegalStateException("CANNOT_OVERRIDE_INVISIBLE_MEMBER should be reported on the corresponding property");
            }

            @Override
            public void nothingToOverride() {
                throw new IllegalStateException("Component functions are OK to override nothing");
            }
        });
    }

    @NotNull
    private static PsiElement findDataModifierForDataClass(@NotNull DeclarationDescriptor dataClass) {
        KtClass classDeclaration = (KtClass) DescriptorToSourceUtils.getSourceFromDescriptor(dataClass);
        if (classDeclaration != null && classDeclaration.getModifierList() != null) {
            PsiElement modifier = classDeclaration.getModifierList().getModifier(KtTokens.DATA_KEYWORD);
            if (modifier != null) {
                return modifier;
            }
        }

        throw new IllegalStateException("No data modifier is found for data class " + dataClass);
    }

    @Nullable
    private static CallableMemberDescriptor findInvisibleOverriddenDescriptor(
            @NotNull CallableMemberDescriptor declared,
            @NotNull ClassDescriptor declaringClass
    ) {
        for (KotlinType supertype : declaringClass.getTypeConstructor().getSupertypes()) {
            Set all = Sets.newLinkedHashSet();
            all.addAll(supertype.getMemberScope().getFunctions(declared.getName(), NoLookupLocation.UNSORTED));
            //noinspection unchecked
            all.addAll((Collection) supertype.getMemberScope().getProperties(declared.getName(), NoLookupLocation.UNSORTED));
            for (CallableMemberDescriptor fromSuper : all) {
                if (OverridingUtil.DEFAULT.isOverridableBy(fromSuper, declared).getResult() == OVERRIDABLE) {
                    if (Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, fromSuper, declared)) {
                        throw new IllegalStateException("Descriptor " + fromSuper + " is overridable by " + declared +
                                                        " and visible but does not appear in its getOverriddenDescriptors()");
                    }
                    return fromSuper;
                }
            }
        }
        return null;
    }

    private void checkParameterOverridesForAllClasses(@NotNull TopDownAnalysisContext c) {
        for (ClassDescriptorWithResolutionScopes classDescriptor : c.getDeclaredClasses().values()) {
            for (DeclarationDescriptor member : classDescriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
                if (member instanceof CallableMemberDescriptor) {
                    checkOverridesForParameters((CallableMemberDescriptor) member);
                }
            }
        }
    }

    private void checkOverridesForParameters(@NotNull CallableMemberDescriptor declared) {
        boolean isDeclaration = declared.getKind() == CallableMemberDescriptor.Kind.DECLARATION;
        if (isDeclaration) {
            // No check if the function is not marked as 'override'
            KtModifierListOwner declaration = (KtModifierListOwner) DescriptorToSourceUtils.descriptorToDeclaration(declared);
            if (declaration != null && !declaration.hasModifier(KtTokens.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 (ValueParameterDescriptor parameterFromSubclass : declared.getValueParameters()) {
            int defaultsInSuper = 0;
            for (ValueParameterDescriptor parameterFromSuperclass : parameterFromSubclass.getOverriddenDescriptors()) {
                if (parameterFromSuperclass.declaresDefaultValue()) {
                    defaultsInSuper++;
                }
            }
            boolean multipleDefaultsInSuper = defaultsInSuper > 1;

            if (isDeclaration) {
                checkNameAndDefaultForDeclaredParameter(parameterFromSubclass, multipleDefaultsInSuper);
            }
            else {
                checkNameAndDefaultForFakeOverrideParameter(declared, parameterFromSubclass, multipleDefaultsInSuper);
            }
        }
    }

    private void checkNameAndDefaultForDeclaredParameter(@NotNull ValueParameterDescriptor descriptor, boolean multipleDefaultsInSuper) {
        KtParameter parameter = (KtParameter) DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
        assert parameter != null : "Declaration not found for parameter: " + descriptor;

        if (descriptor.declaresDefaultValue()) {
            trace.report(DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE.on(parameter));
        }

        if (multipleDefaultsInSuper) {
            trace.report(MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES.on(parameter, descriptor));
        }

        for (ValueParameterDescriptor parameterFromSuperclass : descriptor.getOverriddenDescriptors()) {
            if (shouldReportParameterNameOverrideWarning(descriptor, parameterFromSuperclass)) {
                //noinspection ConstantConditions
                trace.report(PARAMETER_NAME_CHANGED_ON_OVERRIDE.on(
                        parameter,
                        (ClassDescriptor) parameterFromSuperclass.getContainingDeclaration().getContainingDeclaration(),
                        parameterFromSuperclass)
                );
            }
        }
    }

    private void checkNameAndDefaultForFakeOverrideParameter(
            @NotNull CallableMemberDescriptor containingFunction,
            @NotNull ValueParameterDescriptor descriptor,
            boolean multipleDefaultsInSuper
    ) {
        DeclarationDescriptor containingClass = containingFunction.getContainingDeclaration();
        KtClassOrObject classElement = (KtClassOrObject) DescriptorToSourceUtils.descriptorToDeclaration(containingClass);
        assert classElement != null : "Declaration not found for class: " + containingClass;

        if (multipleDefaultsInSuper) {
            trace.report(MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE.on(classElement, descriptor));
        }

        for (ValueParameterDescriptor parameterFromSuperclass : descriptor.getOverriddenDescriptors()) {
            if (shouldReportParameterNameOverrideWarning(descriptor, parameterFromSuperclass)) {
                trace.report(DIFFERENT_NAMES_FOR_THE_SAME_PARAMETER_IN_SUPERTYPES.on(
                        classElement,
                        containingFunction.getOverriddenDescriptors(),
                        parameterFromSuperclass.getIndex() + 1)
                );
            }
        }
    }

    public static boolean shouldReportParameterNameOverrideWarning(
            @NotNull ValueParameterDescriptor parameterFromSubclass,
            @NotNull ValueParameterDescriptor parameterFromSuperclass
    ) {
        return parameterFromSubclass.getContainingDeclaration().hasStableParameterNames() &&
               parameterFromSuperclass.getContainingDeclaration().hasStableParameterNames() &&
               !parameterFromSuperclass.getName().equals(parameterFromSubclass.getName());
    }

    private static boolean checkPropertyKind(@NotNull CallableMemberDescriptor descriptor, boolean isVar) {
        return descriptor instanceof PropertyDescriptor && ((PropertyDescriptor) descriptor).isVar() == isVar;
    }

    private void checkVisibility(@NotNull TopDownAnalysisContext c) {
        for (Map.Entry entry : c.getMembers().entrySet()) {
            checkVisibilityForMember(entry.getKey(), entry.getValue());
        }
    }

    private void checkVisibilityForMember(@NotNull KtDeclaration 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;
            }
        }
    }

    @NotNull
    public static  Collection getDirectlyOverriddenDeclarations(@NotNull D descriptor) {
        Set result = new LinkedHashSet();
        //noinspection unchecked
        for (D overriddenDescriptor : (Collection) descriptor.getOverriddenDescriptors()) {
            CallableMemberDescriptor.Kind kind = overriddenDescriptor.getKind();
            if (kind == DECLARATION) {
                result.add(overriddenDescriptor);
            }
            else if (kind == FAKE_OVERRIDE || kind == DELEGATION) {
                result.addAll(getDirectlyOverriddenDeclarations(overriddenDescriptor));
            }
            else if (kind == SYNTHESIZED) {
                //do nothing
            }
            else {
                throw new AssertionError("Unexpected callable kind " + kind);
            }
        }
        return filterOutOverridden(result);
    }

    @NotNull
    @ReadOnly
    public static  Set getDeepestSuperDeclarations(@NotNull D functionDescriptor) {
        Set overriddenDeclarations = DescriptorUtils.getAllOverriddenDeclarations(functionDescriptor);
        if (overriddenDeclarations.isEmpty()) {
            return Collections.singleton(functionDescriptor);
        }

        return filterOutOverriding(overriddenDeclarations);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy