Forbid calls of Any functions via super if they are overridden as abstract

#KT-38078 Fixed
This commit is contained in:
Mikhail Glukhikh
2022-01-10 16:04:14 +03:00
parent b689bbf5c7
commit 390fa682b9
4 changed files with 39 additions and 41 deletions
@@ -56,26 +56,25 @@ fun BodyResolveComponents.findTypesForSuperCandidates(
private val ARITY_OF_METHODS_OF_ANY = hashMapOf("hashCode" to 0, "equals" to 1, "toString" to 0) private val ARITY_OF_METHODS_OF_ANY = hashMapOf("hashCode" to 0, "equals" to 1, "toString" to 0)
private fun isCallingMethodOfAny(callExpression: FirFunctionCall): Boolean = private fun isCallingMethodOfAny(callExpression: FirFunctionCall): Boolean =
ARITY_OF_METHODS_OF_ANY.getOrElse(callExpression.calleeReference.name.asString(), { -1 }) == callExpression.argumentList.arguments.size ARITY_OF_METHODS_OF_ANY.getOrElse(callExpression.calleeReference.name.asString()) { -1 } == callExpression.argumentList.arguments.size
private fun BodyResolveComponents.resolveSupertypesForMethodOfAny( private fun BodyResolveComponents.resolveSupertypesForMethodOfAny(
supertypes: Collection<ConeKotlinType>, supertypes: Collection<ConeKotlinType>,
calleeName: Name calleeName: Name
): List<ConeKotlinType> { ): List<ConeKotlinType> {
val typesWithConcreteOverride = resolveSupertypesByMembers(supertypes, false) { val typesWithConcreteOverride = resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = false) {
getFunctionMembers(it, calleeName) getFunctionMembers(it, calleeName)
} }
return if (typesWithConcreteOverride.isNotEmpty()) return typesWithConcreteOverride.ifEmpty {
typesWithConcreteOverride
else
listOf(session.builtinTypes.anyType.type) listOf(session.builtinTypes.anyType.type)
}
} }
private fun BodyResolveComponents.resolveSupertypesByCalleeName( private fun BodyResolveComponents.resolveSupertypesByCalleeName(
supertypes: Collection<ConeKotlinType>, supertypes: Collection<ConeKotlinType>,
calleeName: Name calleeName: Name
): List<ConeKotlinType> = ): List<ConeKotlinType> =
resolveSupertypesByMembers(supertypes, true) { resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = true) {
getFunctionMembers(it, calleeName) + getFunctionMembers(it, calleeName) +
getPropertyMembers(it, calleeName) getPropertyMembers(it, calleeName)
} }
@@ -84,13 +83,13 @@ private fun BodyResolveComponents.resolveSupertypesByPropertyName(
supertypes: Collection<ConeKotlinType>, supertypes: Collection<ConeKotlinType>,
propertyName: Name propertyName: Name
): List<ConeKotlinType> = ): List<ConeKotlinType> =
resolveSupertypesByMembers(supertypes, true) { resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = true) {
getPropertyMembers(it, propertyName) getPropertyMembers(it, propertyName)
} }
private inline fun BodyResolveComponents.resolveSupertypesByMembers( private inline fun BodyResolveComponents.resolveSupertypesByMembers(
supertypes: Collection<ConeKotlinType>, supertypes: Collection<ConeKotlinType>,
allowNonConcreteMembers: Boolean, allowNonConcreteInterfaceMembers: Boolean,
getMembers: (ConeKotlinType) -> Collection<FirCallableDeclaration> getMembers: (ConeKotlinType) -> Collection<FirCallableDeclaration>
): List<ConeKotlinType> { ): List<ConeKotlinType> {
val typesWithConcreteMembers = SmartList<ConeKotlinType>() val typesWithConcreteMembers = SmartList<ConeKotlinType>()
@@ -101,24 +100,26 @@ private inline fun BodyResolveComponents.resolveSupertypesByMembers(
if (members.isNotEmpty()) { if (members.isNotEmpty()) {
if (members.any { isConcreteMember(supertype, it) }) if (members.any { isConcreteMember(supertype, it) })
typesWithConcreteMembers.add(supertype) typesWithConcreteMembers.add(supertype)
else else if (members.any { it.dispatchReceiverType?.isAny == false })
typesWithNonConcreteMembers.add(supertype) typesWithNonConcreteMembers.add(supertype)
} }
} }
typesWithConcreteMembers.removeAll { typeWithConcreteMember -> typesWithConcreteMembers.removeAll { typeWithConcreteMember ->
typesWithNonConcreteMembers.any { typeWithNonConcreteMember -> typesWithNonConcreteMembers.any { typeWithNonConcreteMember ->
AbstractTypeChecker.isSubtypeOf(session.typeContext, typeWithNonConcreteMember, typeWithConcreteMember) AbstractTypeChecker.isSubtypeOf(session.typeContext, subType = typeWithNonConcreteMember, superType = typeWithConcreteMember)
} }
} }
return when { return when {
typesWithConcreteMembers.isNotEmpty() -> typesWithConcreteMembers.isNotEmpty() ->
typesWithConcreteMembers typesWithConcreteMembers
allowNonConcreteMembers -> allowNonConcreteInterfaceMembers ->
typesWithNonConcreteMembers typesWithNonConcreteMembers
else -> else ->
emptyList() typesWithNonConcreteMembers.filter {
it is ConeClassLikeType && (it.lookupTag.toSymbol(session) as? FirRegularClassSymbol)?.classKind == ClassKind.CLASS
}
} }
} }
@@ -20,9 +20,7 @@ import com.intellij.util.SmartList
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtCallExpression
@@ -32,6 +30,7 @@ import org.jetbrains.kotlin.psi.KtSuperExpression
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.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.isAny
fun resolveUnqualifiedSuperFromExpressionContext( fun resolveUnqualifiedSuperFromExpressionContext(
@@ -42,8 +41,7 @@ fun resolveUnqualifiedSuperFromExpressionContext(
val parentElement = superExpression.parent val parentElement = superExpression.parent
if (parentElement is KtDotQualifiedExpression) { if (parentElement is KtDotQualifiedExpression) {
val selectorExpression = parentElement.selectorExpression when (val selectorExpression = parentElement.selectorExpression) {
when (selectorExpression) {
is KtCallExpression -> { is KtCallExpression -> {
// super.foo(...): foo can be a function or a property of a callable type // super.foo(...): foo can be a function or a property of a callable type
val calleeExpression = selectorExpression.calleeExpression val calleeExpression = selectorExpression.calleeExpression
@@ -70,7 +68,7 @@ fun resolveUnqualifiedSuperFromExpressionContext(
private val ARITY_OF_METHODS_OF_ANY = hashMapOf("hashCode" to 0, "equals" to 1, "toString" to 0) private val ARITY_OF_METHODS_OF_ANY = hashMapOf("hashCode" to 0, "equals" to 1, "toString" to 0)
private fun isCallingMethodOfAny(callExpression: KtCallExpression, calleeName: Name): Boolean = private fun isCallingMethodOfAny(callExpression: KtCallExpression, calleeName: Name): Boolean =
ARITY_OF_METHODS_OF_ANY.getOrElse(calleeName.asString(), { -1 }) == callExpression.valueArguments.size ARITY_OF_METHODS_OF_ANY.getOrElse(calleeName.asString()) { -1 } == callExpression.valueArguments.size
fun isPossiblyAmbiguousUnqualifiedSuper(superExpression: KtSuperExpression, supertypes: Collection<KotlinType>): Boolean = fun isPossiblyAmbiguousUnqualifiedSuper(superExpression: KtSuperExpression, supertypes: Collection<KotlinType>): Boolean =
supertypes.size > 1 || supertypes.size > 1 ||
@@ -102,30 +100,27 @@ private fun resolveSupertypesForMethodOfAny(
calleeName: Name, calleeName: Name,
anyType: KotlinType anyType: KotlinType
): Collection<KotlinType> { ): Collection<KotlinType> {
val typesWithConcreteOverride = resolveSupertypesByMembers(supertypes, false) { val typesWithConcreteOverride = resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = false) {
getFunctionMembers(it, calleeName, LOOKUP_LOCATION) getFunctionMembers(it, calleeName)
} }
return if (typesWithConcreteOverride.isNotEmpty()) return typesWithConcreteOverride.ifEmpty { listOf(anyType) }
typesWithConcreteOverride
else
listOf(anyType)
} }
private fun resolveSupertypesByCalleeName(supertypes: Collection<KotlinType>, calleeName: Name): Collection<KotlinType> = private fun resolveSupertypesByCalleeName(supertypes: Collection<KotlinType>, calleeName: Name): Collection<KotlinType> =
resolveSupertypesByMembers(supertypes, true) { resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = true) {
getFunctionMembers(it, calleeName, LOOKUP_LOCATION) + getFunctionMembers(it, calleeName) +
getPropertyMembers(it, calleeName, LOOKUP_LOCATION) getPropertyMembers(it, calleeName)
} }
private fun resolveSupertypesByPropertyName(supertypes: Collection<KotlinType>, propertyName: Name): Collection<KotlinType> = private fun resolveSupertypesByPropertyName(supertypes: Collection<KotlinType>, propertyName: Name): Collection<KotlinType> =
resolveSupertypesByMembers(supertypes, true) { resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = true) {
getPropertyMembers(it, propertyName, LOOKUP_LOCATION) getPropertyMembers(it, propertyName)
} }
private inline fun resolveSupertypesByMembers( private inline fun resolveSupertypesByMembers(
supertypes: Collection<KotlinType>, supertypes: Collection<KotlinType>,
allowNonConcreteMembers: Boolean, allowNonConcreteInterfaceMembers: Boolean,
getMembers: (KotlinType) -> Collection<MemberDescriptor> getMembers: (KotlinType) -> Collection<CallableMemberDescriptor>
): Collection<KotlinType> { ): Collection<KotlinType> {
val typesWithConcreteMembers = SmartList<KotlinType>() val typesWithConcreteMembers = SmartList<KotlinType>()
val typesWithNonConcreteMembers = SmartList<KotlinType>() val typesWithNonConcreteMembers = SmartList<KotlinType>()
@@ -135,7 +130,7 @@ private inline fun resolveSupertypesByMembers(
if (members.isNotEmpty()) { if (members.isNotEmpty()) {
if (members.any { isConcreteMember(supertype, it) }) if (members.any { isConcreteMember(supertype, it) })
typesWithConcreteMembers.add(supertype) typesWithConcreteMembers.add(supertype)
else else if (members.any { it.dispatchReceiverParameter?.type?.isAny() == false })
typesWithNonConcreteMembers.add(supertype) typesWithNonConcreteMembers.add(supertype)
} }
} }
@@ -149,20 +144,22 @@ private inline fun resolveSupertypesByMembers(
return when { return when {
typesWithConcreteMembers.isNotEmpty() -> typesWithConcreteMembers.isNotEmpty() ->
typesWithConcreteMembers typesWithConcreteMembers
allowNonConcreteMembers -> allowNonConcreteInterfaceMembers ->
typesWithNonConcreteMembers typesWithNonConcreteMembers
else -> else ->
emptyList() typesWithNonConcreteMembers.filter {
TypeUtils.getClassDescriptor(it)?.kind == ClassKind.CLASS
}
} }
} }
private fun getFunctionMembers(type: KotlinType, name: Name, location: LookupLocation): Collection<MemberDescriptor> = private fun getFunctionMembers(type: KotlinType, name: Name): Collection<CallableMemberDescriptor> =
type.memberScope.getContributedFunctions(name, location) type.memberScope.getContributedFunctions(name, LOOKUP_LOCATION)
private fun getPropertyMembers(type: KotlinType, name: Name, location: LookupLocation): Collection<MemberDescriptor> = private fun getPropertyMembers(type: KotlinType, name: Name): Collection<CallableMemberDescriptor> =
type.memberScope.getContributedVariables(name, location).filterIsInstanceTo(SmartList<MemberDescriptor>()) type.memberScope.getContributedVariables(name, LOOKUP_LOCATION).filterIsInstanceTo(SmartList())
private fun isConcreteMember(supertype: KotlinType, memberDescriptor: MemberDescriptor): Boolean { private fun isConcreteMember(supertype: KotlinType, memberDescriptor: CallableMemberDescriptor): Boolean {
// "Concrete member" is a function or a property that is not abstract, // "Concrete member" is a function or a property that is not abstract,
// and is not an implicit fake override for a method of Any on an interface. // and is not an implicit fake override for a method of Any on an interface.
@@ -170,7 +167,7 @@ private fun isConcreteMember(supertype: KotlinType, memberDescriptor: MemberDesc
return false return false
val classDescriptorForSupertype = TypeUtils.getClassDescriptor(supertype) val classDescriptorForSupertype = TypeUtils.getClassDescriptor(supertype)
val memberKind = (memberDescriptor as CallableMemberDescriptor).kind val memberKind = memberDescriptor.kind
if (classDescriptorForSupertype?.kind == ClassKind.INTERFACE && memberKind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { if (classDescriptorForSupertype?.kind == ClassKind.INTERFACE && memberKind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
// We have a fake override on interface. It should have a dispatch receiver, which should not be Any. // We have a fake override on interface. It should have a dispatch receiver, which should not be Any.
val dispatchReceiverType = memberDescriptor.dispatchReceiverParameter?.type ?: return false val dispatchReceiverType = memberDescriptor.dispatchReceiverParameter?.type ?: return false
@@ -22,5 +22,5 @@ abstract class A {
interface I interface I
class B : A(), I { // I is necessary here class B : A(), I { // I is necessary here
override fun hashCode() = super.hashCode() override fun hashCode() = super.<!ABSTRACT_SUPER_CALL!>hashCode<!>()
} }
@@ -22,5 +22,5 @@ abstract class A {
interface I interface I
class B : A(), I { // I is necessary here class B : A(), I { // I is necessary here
override fun hashCode() = super.hashCode() override fun hashCode() = super.<!ABSTRACT_SUPER_CALL!>hashCode<!>()
} }