Refactor DelegationChecker and related code

- move getDelegates from CodegenUtil to DelegationResolver, reuse it in
  DelegationChecker
- use getAllOverriddenDescriptors + filterOutOverridden instead of a manual DFS
This commit is contained in:
Alexander Udalov
2016-12-19 12:34:26 +03:00
parent 2dd7d89048
commit 482b43631c
8 changed files with 122 additions and 109 deletions
@@ -24,51 +24,10 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.MemberComparator
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.utils.keysToMapExceptNulls
object CodegenUtil { object CodegenUtil {
// class Foo : Bar by baz
// descriptor = Foo
// toInterface = Bar
// delegateExpressionType = typeof(baz)
// return Map<member of Foo, corresponding member of typeOf(baz)>
@JvmStatic
fun getDelegates(
descriptor: ClassDescriptor,
toInterface: ClassDescriptor,
delegateExpressionType: KotlinType? = null
): Map<CallableMemberDescriptor, CallableDescriptor> {
if (delegateExpressionType?.isDynamic() ?: false) return emptyMap()
return descriptor.defaultType.memberScope.getContributedDescriptors().asSequence()
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.DELEGATION }
.asIterable()
.sortedWith(MemberComparator.INSTANCE)
.keysToMapExceptNulls { delegatingMember ->
val actualDelegates = DescriptorUtils.getAllOverriddenDescriptors(delegatingMember)
.filter { it.containingDeclaration == toInterface }
.map { overriddenDescriptor ->
val scope = (delegateExpressionType ?: toInterface.defaultType).memberScope
val name = overriddenDescriptor.name
// this is the actual member of delegateExpressionType that we are delegating to
(scope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND) +
scope.getContributedVariables(name, NoLookupLocation.FROM_BACKEND))
.firstOrNull { it == overriddenDescriptor || OverridingUtil.overrides(it, overriddenDescriptor) }
}
assert(actualDelegates.size <= 1) { "Many delegates found for $delegatingMember: $actualDelegates" }
actualDelegates.firstOrNull()
}
}
@JvmStatic @JvmStatic
fun getDelegatePropertyIfAny( fun getDelegatePropertyIfAny(
expression: KtExpression, classDescriptor: ClassDescriptor, bindingContext: BindingContext expression: KtExpression, classDescriptor: ClassDescriptor, bindingContext: BindingContext
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DelegationResolver;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
@@ -1655,18 +1656,21 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
fieldInfo.name, fieldInfo.type.getDescriptor(), /*TODO*/null, null); fieldInfo.name, fieldInfo.type.getDescriptor(), /*TODO*/null, null);
} }
private void generateDelegates(ClassDescriptor toTrait, KotlinType delegateExpressionType, DelegationFieldsInfo.Field field) { private void generateDelegates(
for (Map.Entry<CallableMemberDescriptor, CallableDescriptor> entry : @NotNull ClassDescriptor toInterface,
CodegenUtil.getDelegates(descriptor, toTrait, delegateExpressionType).entrySet()) { @Nullable KotlinType delegateExpressionType,
CallableMemberDescriptor callableMemberDescriptor = entry.getKey(); @NotNull DelegationFieldsInfo.Field field
CallableDescriptor delegateTo = entry.getValue(); ) {
if (callableMemberDescriptor instanceof PropertyDescriptor) { for (Map.Entry<CallableMemberDescriptor, CallableMemberDescriptor> entry : DelegationResolver.Companion.getDelegates(
propertyCodegen descriptor, toInterface, delegateExpressionType).entrySet()
.genDelegate((PropertyDescriptor) callableMemberDescriptor, (PropertyDescriptor) delegateTo, field.getStackValue()); ) {
CallableMemberDescriptor member = entry.getKey();
CallableMemberDescriptor delegateTo = entry.getValue();
if (member instanceof PropertyDescriptor) {
propertyCodegen.genDelegate((PropertyDescriptor) member, (PropertyDescriptor) delegateTo, field.getStackValue());
} }
else if (callableMemberDescriptor instanceof FunctionDescriptor) { else if (member instanceof FunctionDescriptor) {
functionCodegen functionCodegen.genDelegate((FunctionDescriptor) member, (FunctionDescriptor) delegateTo, field.getStackValue());
.genDelegate((FunctionDescriptor) callableMemberDescriptor, (FunctionDescriptor) delegateTo, field.getStackValue());
} }
} }
} }
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION
import org.jetbrains.kotlin.diagnostics.Errors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED import org.jetbrains.kotlin.diagnostics.Errors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.psi.KtPureClassOrObject
@@ -27,6 +28,8 @@ import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Res
import org.jetbrains.kotlin.resolve.lazy.DelegationFilter import org.jetbrains.kotlin.resolve.lazy.DelegationFilter
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.utils.keysToMapExceptNulls
class DelegationResolver<T : CallableMemberDescriptor> private constructor( class DelegationResolver<T : CallableMemberDescriptor> private constructor(
private val classOrObject: KtPureClassOrObject, private val classOrObject: KtPureClassOrObject,
@@ -124,5 +127,40 @@ class DelegationResolver<T : CallableMemberDescriptor> private constructor(
private fun isOverridableBy(memberOne: CallableDescriptor, memberTwo: CallableDescriptor): Boolean = private fun isOverridableBy(memberOne: CallableDescriptor, memberTwo: CallableDescriptor): Boolean =
OverridingUtil.DEFAULT.isOverridableBy(memberOne, memberTwo, null).result == OVERRIDABLE OverridingUtil.DEFAULT.isOverridableBy(memberOne, memberTwo, null).result == OVERRIDABLE
// class Foo : Bar by baz
// descriptor = Foo
// toInterface = Bar
// delegateExpressionType = typeof(baz)
// return Map<member of Foo, corresponding member of typeOf(baz)>
fun getDelegates(
descriptor: ClassDescriptor,
toInterface: ClassDescriptor,
delegateExpressionType: KotlinType? = null
): Map<CallableMemberDescriptor, CallableMemberDescriptor> {
if (delegateExpressionType?.isDynamic() ?: false) return emptyMap()
val delegatedMembers = descriptor.defaultType.memberScope.getContributedDescriptors().asSequence()
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.DELEGATION }
.asIterable()
.sortedWith(MemberComparator.INSTANCE)
return delegatedMembers
.keysToMapExceptNulls { delegatingMember ->
val actualDelegates = DescriptorUtils.getAllOverriddenDescriptors(delegatingMember)
.filter { it.containingDeclaration == toInterface }
.map { overriddenDescriptor ->
val scope = (delegateExpressionType ?: toInterface.defaultType).memberScope
val name = overriddenDescriptor.name
// this is the actual member of delegateExpressionType that we are delegating to
(scope.getContributedFunctions(name, NoLookupLocation.WHEN_CHECK_OVERRIDES) +
scope.getContributedVariables(name, NoLookupLocation.WHEN_CHECK_OVERRIDES))
.firstOrNull { it == overriddenDescriptor || OverridingUtil.overrides(it, overriddenDescriptor) }
}
actualDelegates.firstOrNull()
}
}
} }
} }
@@ -22,16 +22,13 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors.DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE import org.jetbrains.kotlin.diagnostics.Errors.DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegationResolver
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.MemberComparator
import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.keysToMapExceptNulls
class DelegationChecker : SimpleDeclarationChecker { class DelegationChecker : SimpleDeclarationChecker {
override fun check( override fun check(
@@ -43,16 +40,12 @@ class DelegationChecker : SimpleDeclarationChecker {
if (descriptor !is ClassDescriptor) return if (descriptor !is ClassDescriptor) return
if (declaration !is KtClassOrObject) return if (declaration !is KtClassOrObject) return
val delegationDescriptors = descriptor.defaultType.memberScope.getContributedDescriptors().
filterIsInstance<CallableMemberDescriptor>().filter { it.kind == CallableMemberDescriptor.Kind.DELEGATION }.sortedWith(MemberComparator.INSTANCE)
for (specifier in declaration.superTypeListEntries) { for (specifier in declaration.superTypeListEntries) {
if (specifier is KtDelegatedSuperTypeEntry) { if (specifier is KtDelegatedSuperTypeEntry) {
val superType = specifier.typeReference?.let { bindingContext.get(BindingContext.TYPE, it) } ?: continue val superType = specifier.typeReference?.let { bindingContext.get(BindingContext.TYPE, it) } ?: continue
val superTypeDescriptor = superType.constructor.declarationDescriptor as? ClassDescriptor ?: continue val superTypeDescriptor = superType.constructor.declarationDescriptor as? ClassDescriptor ?: continue
val delegates = getDelegates(delegationDescriptors, superTypeDescriptor) for ((delegated, delegatedTo) in DelegationResolver.getDelegates(descriptor, superTypeDescriptor)) {
delegates.forEach { (delegated, delegatedTo) ->
checkDescriptor(declaration, delegated, delegatedTo, diagnosticHolder) checkDescriptor(declaration, delegated, delegatedTo, diagnosticHolder)
} }
} }
@@ -65,57 +58,16 @@ class DelegationChecker : SimpleDeclarationChecker {
delegatedToDescriptor: CallableMemberDescriptor, delegatedToDescriptor: CallableMemberDescriptor,
diagnosticHolder: DiagnosticSink diagnosticHolder: DiagnosticSink
) { ) {
val reachableFromDelegated = findAllReachableDeclarations(delegatedDescriptor) val reachableFromDelegated =
reachableFromDelegated.remove(delegatedDescriptor.original) OverridingUtil.filterOutOverridden(
val toRemove = linkedSetOf<CallableMemberDescriptor>() DescriptorUtils.getAllOverriddenDescriptors(delegatedDescriptor).filter { it.kind.isReal }.toSet()
for (declaration in reachableFromDelegated) { ) - DescriptorUtils.unwrapFakeOverride(delegatedToDescriptor).original
val reachable = findAllReachableDeclarations(declaration.original)
reachable.remove(declaration)
toRemove.addAll(reachable)
}
reachableFromDelegated.removeAll(toRemove)
reachableFromDelegated.remove(DescriptorUtils.unwrapFakeOverride(delegatedToDescriptor).original)
val nonAbstractReachable = reachableFromDelegated.filter { it.modality != Modality.ABSTRACT } val nonAbstractReachable = reachableFromDelegated.filter { it.modality != Modality.ABSTRACT }
if (nonAbstractReachable.isNotEmpty()) { if (nonAbstractReachable.isNotEmpty()) {
/*In case of MANY_IMPL_MEMBER_NOT_IMPLEMENTED error there could be several elements otherwise only one*/ /*In case of MANY_IMPL_MEMBER_NOT_IMPLEMENTED error there could be several elements otherwise only one*/
diagnosticHolder.report(DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE.on(classDeclaration, delegatedDescriptor, nonAbstractReachable.toList())) diagnosticHolder.report(DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE.on(classDeclaration, delegatedDescriptor, nonAbstractReachable))
} }
} }
fun getDelegates(
delegatedMethods: Iterable<CallableMemberDescriptor>,
toInterface: ClassDescriptor
): Map<CallableMemberDescriptor, CallableMemberDescriptor> {
return delegatedMethods
.keysToMapExceptNulls { delegatingMember ->
val actualDelegates = DescriptorUtils.getAllOverriddenDescriptors(delegatingMember)
.filter { it.containingDeclaration == toInterface }
.map { overriddenDescriptor ->
val scope = toInterface.defaultType.memberScope
val name = overriddenDescriptor.name
// this is the actual member of delegateExpressionType that we are delegating to
(scope.getContributedFunctions(name, NoLookupLocation.WHEN_CHECK_OVERRIDES) +
scope.getContributedVariables(name, NoLookupLocation.WHEN_CHECK_OVERRIDES))
.firstOrNull { it == overriddenDescriptor || OverridingUtil.overrides(it, overriddenDescriptor) }
}
actualDelegates.firstOrNull() as? CallableMemberDescriptor
}
}
}
private fun findAllReachableDeclarations(memberDescriptor: CallableMemberDescriptor): MutableSet<CallableMemberDescriptor> {
val collector = object : DFS.NodeHandlerWithListResult<CallableMemberDescriptor, CallableMemberDescriptor>() {
override fun afterChildren(current: CallableMemberDescriptor) {
if (current.kind.isReal) {
result.add(current.original)
}
}
}
DFS.dfs(listOf(memberDescriptor), { it.overriddenDescriptors }, collector)
return java.util.HashSet(collector.result())
} }
@@ -0,0 +1,13 @@
interface Base {
fun test() = "Base"
}
class Delegate : Base
abstract class Middle : Base {
override fun test() = "MyClass"
}
abstract class MyClass : Middle()
<!DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE!>class A : MyClass(), Base by Delegate()<!>
@@ -0,0 +1,40 @@
package
public final class A : MyClass, Base {
public constructor A()
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*delegation*/ fun test(): kotlin.String
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Base {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open fun test(): kotlin.String
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Delegate : Base {
public constructor Delegate()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun test(): kotlin.String
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public abstract class Middle : Base {
public constructor Middle()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ fun test(): kotlin.String
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public abstract class MyClass : Middle {
public constructor MyClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun test(): kotlin.String
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -6271,6 +6271,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("fakeOverrideInTheMiddle.kt")
public void testFakeOverrideInTheMiddle() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegationBy/fakeOverrideInTheMiddle.kt");
doTest(fileName);
}
@TestMetadata("generic.kt") @TestMetadata("generic.kt")
public void testGeneric() throws Exception { public void testGeneric() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegationBy/generic.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegationBy/generic.kt");
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.translateFunctio
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
import org.jetbrains.kotlin.psi.KtSuperTypeListEntry import org.jetbrains.kotlin.psi.KtSuperTypeListEntry
import org.jetbrains.kotlin.resolve.DelegationResolver
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
@@ -42,7 +43,7 @@ class DelegationTranslator(
BindingUtils.getClassDescriptor(context.bindingContext(), classDeclaration) BindingUtils.getClassDescriptor(context.bindingContext(), classDeclaration)
private val delegationBySpecifiers = private val delegationBySpecifiers =
classDeclaration.getSuperTypeListEntries().filterIsInstance<KtDelegatedSuperTypeEntry>() classDeclaration.superTypeListEntries.filterIsInstance<KtDelegatedSuperTypeEntry>()
private class Field (val name: JsName, val generateField: Boolean) private class Field (val name: JsName, val generateField: Boolean)
private val fields = mutableMapOf<KtDelegatedSuperTypeEntry, Field>() private val fields = mutableMapOf<KtDelegatedSuperTypeEntry, Field>()
@@ -91,7 +92,7 @@ class DelegationTranslator(
CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext()) CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext())
private fun generateDelegates(toClass: ClassDescriptor, field: Field) { private fun generateDelegates(toClass: ClassDescriptor, field: Field) {
for ((descriptor, overriddenDescriptor) in CodegenUtil.getDelegates(classDescriptor, toClass)) { for ((descriptor, overriddenDescriptor) in DelegationResolver.getDelegates(classDescriptor, toClass)) {
when (descriptor) { when (descriptor) {
is PropertyDescriptor -> is PropertyDescriptor ->
generateDelegateCallForPropertyMember(descriptor, field.name) generateDelegateCallForPropertyMember(descriptor, field.name)