Forbid calls of Any functions via super if they are overridden as abstract
#KT-38078 Fixed
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-27
@@ -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
|
||||||
|
|||||||
+1
-1
@@ -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<!>()
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -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<!>()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user