K1: Deprecate incorrect callable references resolution behavior

^KT-54316 Related
^KT-54832 Fixed
This commit is contained in:
Denis.Zharkov
2022-11-04 19:28:21 +01:00
committed by Space Team
parent a3fd63fb1d
commit 2953e600ff
21 changed files with 485 additions and 20 deletions
@@ -889,6 +889,7 @@ public interface Errors {
DiagnosticFactory0<KtExpression> RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParenthesizedExpression> PARENTHESIZED_COMPANION_LHS_DEPRECATION = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> INCORRECT_CALLABLE_REFERENCE_RESOLUTION_FOR_COMPANION_LHS = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> RESOLUTION_TO_PRIVATE_CONSTRUCTOR_OF_SEALED_CLASS = DiagnosticFactory0.create(WARNING);
@@ -460,6 +460,11 @@ public class DefaultErrorMessages {
MAP.put(PARENTHESIZED_COMPANION_LHS_DEPRECATION, "Access to companion object through parenthesized class name is deprecated. Please, add explicit Companion qualifier.");
MAP.put(
INCORRECT_CALLABLE_REFERENCE_RESOLUTION_FOR_COMPANION_LHS,
"Callable reference to the companion's member is incorrectly resolved as unbound. Please, add explicit Companion qualifier to the left-hand-side. See https://youtrack.jetbrains.com/issue/KT-54316 for details"
);
MAP.put(RESOLUTION_TO_PRIVATE_CONSTRUCTOR_OF_SEALED_CLASS, "The private constructor of a sealed class will become inaccessible here in future. See https://youtrack.jetbrains.com/issue/KT-44866 for details");
MAP.put(
@@ -70,7 +70,7 @@ private val DEFAULT_CALL_CHECKERS = listOf(
ReferencingToUnderscoreNamedParameterOfCatchBlockChecker, VarargWrongExecutionOrderChecker, SelfCallInNestedObjectConstructorChecker,
NewSchemeOfIntegerOperatorResolutionChecker, EnumEntryVsCompanionPriorityCallChecker, CompanionInParenthesesLHSCallChecker,
ResolutionToPrivateConstructorOfSealedClassChecker, EqualityCallChecker, UnsupportedUntilOperatorChecker,
BuilderInferenceAssignmentChecker, IncorrectCapturedApproximationCallChecker,
BuilderInferenceAssignmentChecker, IncorrectCapturedApproximationCallChecker, CompanionIncorrectlyUnboundedWhenUsedAsLHSCallChecker,
)
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(
@@ -13,8 +13,11 @@ import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtParenthesizedExpression
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.context.CallPosition
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
/**
* Deprecate callable references in a form of (SomeClass)::name when SomeClass has a companion
@@ -27,16 +30,49 @@ object CompanionInParenthesesLHSCallChecker : CallChecker {
val unwrappedLhs = parenthesizedExpression.expression ?: return
val expressionReceiver = resolvedCall.call.explicitReceiver as? ExpressionReceiver ?: return
val referencedClass = expressionReceiver.type.constructor.declarationDescriptor as? ClassDescriptor ?: return
if (!referencedClass.isCompanionObject) return
// We should also consider cases like (package.MyClassWithCompanion)::foo
val simpleReference =
((unwrappedLhs as? KtDotQualifiedExpression)?.selectorExpression ?: unwrappedLhs) as? KtReferenceExpression
?: return
if (context.trace.bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, simpleReference] == null) return
if (!isReferenceToShortFormCompanion(expressionReceiver, unwrappedLhs, context)) return
context.trace.report(Errors.PARENTHESIZED_COMPANION_LHS_DEPRECATION.on(parenthesizedExpression))
}
}
/**
* Report warnings on top-level callable references (one that are not nested in any call) that have short-formed LHS resolved to companion
* Because they have wrong function type shape at K1
* (see relevant testData)
*/
object CompanionIncorrectlyUnboundedWhenUsedAsLHSCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val callableReference = resolvedCall.call.callElement.parent as? KtCallableReferenceExpression ?: return
val classQualifier = resolvedCall.call.explicitReceiver as? ClassQualifier ?: return
val dispatchReceiver = resolvedCall.dispatchReceiver ?: return
if (dispatchReceiver != classQualifier.classValueReceiver) return
// Only top-level callable references may have callPosition like this
// References nested in calls have CallPosition.Unknown
if (context.resolutionContext.callPosition !is CallPosition.CallableReferenceRhs) return
val referencedClass = dispatchReceiver.type.constructor.declarationDescriptor as? ClassDescriptor ?: return
if (!referencedClass.isCompanionObject) return
context.trace.report(Errors.INCORRECT_CALLABLE_REFERENCE_RESOLUTION_FOR_COMPANION_LHS.on(callableReference))
}
}
private fun isReferenceToShortFormCompanion(
receiver: ReceiverValue,
lhs: PsiElement?,
context: CallCheckerContext
): Boolean {
val referencedClass = receiver.type.constructor.declarationDescriptor as? ClassDescriptor ?: return false
if (!referencedClass.isCompanionObject) return false
// We should also consider cases like (package.MyClassWithCompanion)::foo
val simpleReference =
((lhs as? KtDotQualifiedExpression)?.selectorExpression ?: lhs) as? KtReferenceExpression
?: return false
return context.trace.bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, simpleReference] != null
}