Provide migration of Any functions via super in abstract case

Related to KT-38078
This commit is contained in:
Mikhail Glukhikh
2022-01-11 13:29:31 +03:00
parent 390fa682b9
commit fdf336042b
12 changed files with 68 additions and 29 deletions
@@ -120,6 +120,7 @@ public interface BindingContext {
WritableSlice<KtExpression, DoubleColonLHS> DOUBLE_COLON_LHS = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<KtSuperExpression, KotlinType> THIS_TYPE_FOR_SUPER_EXPRESSION = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<KtSuperExpression, Boolean> SUPER_EXPRESSION_FROM_ANY_MIGRATION = Slices.createSimpleSlice();
WritableSlice<KtReferenceExpression, DeclarationDescriptor> REFERENCE_TARGET = new BasicWritableSlice<>(DO_NOTHING);
// if 'A' really means 'A.Companion' then this slice stores class descriptor for A, REFERENCE_TARGET stores descriptor Companion in this case
@@ -76,12 +76,22 @@ class DiagnosticReporterByTrackingStrategy(
(diagnostic as NoValueForParameter).parameterDescriptor
)
InstantiationOfAbstractClass::class.java -> tracingStrategy.instantiationOfAbstractClass(trace)
AbstractSuperCall::class.java -> tracingStrategy.abstractSuperCall(trace)
AbstractSuperCall::class.java -> {
val superExpression = (diagnostic as AbstractSuperCall).receiver.psiExpression as? KtSuperExpression
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ForbidSuperDelegationToAbstractAnyMethod) ||
superExpression == null ||
trace[BindingContext.SUPER_EXPRESSION_FROM_ANY_MIGRATION, superExpression] != true
) {
tracingStrategy.abstractSuperCall(trace)
} else {
tracingStrategy.abstractSuperCallWarning(trace)
}
}
AbstractFakeOverrideSuperCall::class.java -> {
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ForbidSuperDelegationToAbstractFakeOverride)) {
tracingStrategy.abstractSuperCall(trace)
} else {
tracingStrategy.abstractFakeOverrideSuperCall(trace)
tracingStrategy.abstractSuperCallWarning(trace)
}
}
NonApplicableCallForBuilderInferenceDiagnostic::class.java -> {
@@ -143,7 +143,7 @@ public abstract class AbstractTracingStrategy implements TracingStrategy {
}
@Override
public void abstractFakeOverrideSuperCall(@NotNull BindingTrace trace) {
public void abstractSuperCallWarning(@NotNull BindingTrace trace) {
trace.report(ABSTRACT_SUPER_CALL_WARNING.on(reference));
}
@@ -148,7 +148,7 @@ public interface TracingStrategy {
void abstractSuperCall(@NotNull BindingTrace trace);
default void abstractFakeOverrideSuperCall(@NotNull BindingTrace trace) {
default void abstractSuperCallWarning(@NotNull BindingTrace trace) {
abstractSuperCall(trace);
}
@@ -22,6 +22,7 @@ import com.intellij.psi.StubBasedPsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import kotlin.Pair;
import kotlin.TuplesKt;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
@@ -46,7 +47,6 @@ import org.jetbrains.kotlin.resolve.*;
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
import org.jetbrains.kotlin.resolve.calls.CallExpressionResolver;
import org.jetbrains.kotlin.resolve.calls.util.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.checkers.*;
import org.jetbrains.kotlin.resolve.calls.model.DataFlowInfoForArgumentsImpl;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
@@ -62,13 +62,13 @@ import org.jetbrains.kotlin.resolve.calls.tasks.OldResolutionCandidate;
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
import org.jetbrains.kotlin.resolve.calls.tower.NewAbstractResolvedCall;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.calls.util.CallUtilKt;
import org.jetbrains.kotlin.resolve.checkers.UnderscoreChecker;
import org.jetbrains.kotlin.resolve.constants.*;
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
import org.jetbrains.kotlin.resolve.scopes.receivers.ContextReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt;
import org.jetbrains.kotlin.types.*;
@@ -512,9 +512,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
else {
if (UnqualifiedSuperKt.isPossiblyAmbiguousUnqualifiedSuper(expression, supertypes)) {
Collection<KotlinType> supertypesResolvedFromContext =
Pair<Collection<KotlinType>, Boolean> supertypesResolvedFromContextWithEqualsMigration =
UnqualifiedSuperKt.resolveUnqualifiedSuperFromExpressionContext(
expression, supertypes, components.builtIns.getAnyType());
Collection<KotlinType> supertypesResolvedFromContext = supertypesResolvedFromContextWithEqualsMigration.getFirst();
if (supertypesResolvedFromContextWithEqualsMigration.getSecond()) {
context.trace.record(SUPER_EXPRESSION_FROM_ANY_MIGRATION, expression, true);
}
if (supertypesResolvedFromContext.size() == 1) {
KotlinType singleResolvedType = supertypesResolvedFromContext.iterator().next();
result = substitutor.substitute(singleResolvedType, Variance.INVARIANT);
@@ -37,7 +37,7 @@ fun resolveUnqualifiedSuperFromExpressionContext(
superExpression: KtSuperExpression,
supertypes: Collection<KotlinType>,
anyType: KotlinType
): Collection<KotlinType> {
): Pair<Collection<KotlinType>, Boolean> {
val parentElement = superExpression.parent
if (parentElement is KtDotQualifiedExpression) {
@@ -50,19 +50,19 @@ fun resolveUnqualifiedSuperFromExpressionContext(
return if (isCallingMethodOfAny(selectorExpression, calleeName)) {
resolveSupertypesForMethodOfAny(supertypes, calleeName, anyType)
} else {
resolveSupertypesByCalleeName(supertypes, calleeName)
resolveSupertypesByCalleeName(supertypes, calleeName) to false
}
}
}
is KtSimpleNameExpression -> {
// super.x: x can be a property only
// NB there are no properties in kotlin.Any
return resolveSupertypesByPropertyName(supertypes, selectorExpression.getReferencedNameAsName())
return resolveSupertypesByPropertyName(supertypes, selectorExpression.getReferencedNameAsName()) to false
}
}
}
return emptyList()
return emptyList<KotlinType>() to false
}
private val ARITY_OF_METHODS_OF_ANY = hashMapOf("hashCode" to 0, "equals" to 1, "toString" to 0)
@@ -99,29 +99,29 @@ private fun resolveSupertypesForMethodOfAny(
supertypes: Collection<KotlinType>,
calleeName: Name,
anyType: KotlinType
): Collection<KotlinType> {
val typesWithConcreteOverride = resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = false) {
): Pair<Collection<KotlinType>, Boolean> {
val (typesWithConcreteOverride, isEqualsMigration) = resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = false) {
getFunctionMembers(it, calleeName)
}
return typesWithConcreteOverride.ifEmpty { listOf(anyType) }
return typesWithConcreteOverride.ifEmpty { listOf(anyType) } to isEqualsMigration
}
private fun resolveSupertypesByCalleeName(supertypes: Collection<KotlinType>, calleeName: Name): Collection<KotlinType> =
resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = true) {
getFunctionMembers(it, calleeName) +
getPropertyMembers(it, calleeName)
}
}.first
private fun resolveSupertypesByPropertyName(supertypes: Collection<KotlinType>, propertyName: Name): Collection<KotlinType> =
resolveSupertypesByMembers(supertypes, allowNonConcreteInterfaceMembers = true) {
getPropertyMembers(it, propertyName)
}
}.first
private inline fun resolveSupertypesByMembers(
supertypes: Collection<KotlinType>,
allowNonConcreteInterfaceMembers: Boolean,
getMembers: (KotlinType) -> Collection<CallableMemberDescriptor>
): Collection<KotlinType> {
): Pair<Collection<KotlinType>, Boolean> {
val typesWithConcreteMembers = SmartList<KotlinType>()
val typesWithNonConcreteMembers = SmartList<KotlinType>()
@@ -143,13 +143,13 @@ private inline fun resolveSupertypesByMembers(
return when {
typesWithConcreteMembers.isNotEmpty() ->
typesWithConcreteMembers
typesWithConcreteMembers to false
allowNonConcreteInterfaceMembers ->
typesWithNonConcreteMembers
typesWithNonConcreteMembers to false
else ->
typesWithNonConcreteMembers.filter {
TypeUtils.getClassDescriptor(it)?.kind == ClassKind.CLASS
}
} to true
}
}
@@ -812,9 +812,8 @@ internal object CheckSuperExpressionCallPart : ResolutionPart() {
if (callComponents.statelessCallbacks.isSuperExpression(resolvedCall.dispatchReceiverArgument)) {
if (candidateDescriptor is CallableMemberDescriptor) {
if (candidateDescriptor.modality == Modality.ABSTRACT) {
addDiagnostic(AbstractSuperCall)
}
if (candidateDescriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
addDiagnostic(AbstractSuperCall(resolvedCall.dispatchReceiverArgument!!))
} else if (candidateDescriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
candidateDescriptor.overriddenDescriptors.size > 1
) {
if (candidateDescriptor.overriddenDescriptors.firstOrNull { !it.isInsideInterface }?.modality == Modality.ABSTRACT) {
@@ -202,7 +202,7 @@ object InstantiationOfAbstractClass : KotlinCallDiagnostic(RUNTIME_ERROR) {
override fun report(reporter: DiagnosticReporter) = reporter.onCall(this)
}
object AbstractSuperCall : KotlinCallDiagnostic(RUNTIME_ERROR) {
class AbstractSuperCall(val receiver: SimpleKotlinCallArgument) : KotlinCallDiagnostic(RUNTIME_ERROR) {
override fun report(reporter: DiagnosticReporter) {
reporter.onCall(this)
}
@@ -0,0 +1,25 @@
// !LANGUAGE: -ForbidSuperDelegationToAbstractFakeOverride -ForbidSuperDelegationToAbstractAnyMethod
interface Foo {
fun check(): String = "OK"
}
abstract class Base {
abstract fun check(): String
}
abstract class Derived : Base(), Foo
class Derived2 : Derived() {
override fun check(): String {
super<Derived>.<!ABSTRACT_SUPER_CALL_WARNING!>check<!>()
return super.<!ABSTRACT_SUPER_CALL_WARNING!>check<!>()
}
}
abstract class A {
abstract override fun hashCode(): Int
}
interface I
class B : A(), I { // I is necessary here
override fun hashCode() = super.<!ABSTRACT_SUPER_CALL!>hashCode<!>()
}
@@ -1,5 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: -ForbidSuperDelegationToAbstractFakeOverride
// !LANGUAGE: -ForbidSuperDelegationToAbstractFakeOverride -ForbidSuperDelegationToAbstractAnyMethod
interface Foo {
fun check(): String = "OK"
}
@@ -22,5 +21,5 @@ abstract class A {
interface I
class B : A(), I { // I is necessary here
override fun hashCode() = super.<!ABSTRACT_SUPER_CALL!>hashCode<!>()
override fun hashCode() = super.<!ABSTRACT_SUPER_CALL_WARNING!>hashCode<!>()
}
@@ -1,5 +1,5 @@
// FIR_IDENTICAL
// !LANGUAGE: +ForbidSuperDelegationToAbstractFakeOverride
// !LANGUAGE: +ForbidSuperDelegationToAbstractFakeOverride +ForbidSuperDelegationToAbstractAnyMethod
interface Foo {
fun check(): String = "OK"
}
@@ -253,7 +253,8 @@ enum class LanguageFeature(
ProhibitConfusingSyntaxInWhenBranches(KOTLIN_1_8, kind = BUG_FIX), // KT-48385
UseConsistentRulesForPrivateConstructorsOfSealedClasses(sinceVersion = KOTLIN_1_8, kind = BUG_FIX), // KT-44866
ProgressionsChangingResolve(KOTLIN_1_8), // KT-49276
ForbidSuperDelegationToAbstractFakeOverride(KOTLIN_1_8), // KT-49017 (+ KT-38078)
ForbidSuperDelegationToAbstractFakeOverride(KOTLIN_1_8), // KT-49017
ForbidSuperDelegationToAbstractAnyMethod(KOTLIN_1_8), // KT-38078
// 1.9