Refactor CallableReferencesResolutionUtils.kt

Get rid of trace & reportOn parameters of
createReflectionTypeForCallableDescriptor: move the two checks that required
them to DoubleColonExpressionResolver and combine with other checks into a
single function that checks the validity of the referenced symbol. This also
makes these checks reported only once when invalid expressions are passed as
function arguments (previously they were also reported from
getResolvedCallableReferenceShapeType).

Also inline getReflectionTypeForCandidateDescriptor after this, and refactor
its usages
This commit is contained in:
Alexander Udalov
2016-06-21 17:32:29 +03:00
parent cf630e01db
commit 80bf6e1bee
8 changed files with 74 additions and 98 deletions
@@ -22,12 +22,12 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.diagnostics.Errors.CALLABLE_REFERENCE_LHS_NOT_A_CLASS
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.CallResolver import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
@@ -37,6 +37,8 @@ import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil
import org.jetbrains.kotlin.resolve.calls.util.CallMaker import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.createFunctionType
import org.jetbrains.kotlin.resolve.createValueParametersForInvokeInFunctionType
import org.jetbrains.kotlin.resolve.scopes.BaseLexicalScope import org.jetbrains.kotlin.resolve.scopes.BaseLexicalScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
@@ -159,7 +161,7 @@ fun resolvePossiblyAmbiguousCallableReference(
return null return null
} }
private fun bindFunctionReference(expression: KtCallableReferenceExpression, type: KotlinType, context: ResolutionContext<*>) { internal fun bindFunctionReference(expression: KtCallableReferenceExpression, type: KotlinType, context: ResolutionContext<*>) {
val functionDescriptor = AnonymousFunctionDescriptor( val functionDescriptor = AnonymousFunctionDescriptor(
context.scope.ownerDescriptor, context.scope.ownerDescriptor,
Annotations.EMPTY, Annotations.EMPTY,
@@ -178,29 +180,20 @@ private fun bindFunctionReference(expression: KtCallableReferenceExpression, typ
context.trace.record(BindingContext.FUNCTION, expression, functionDescriptor) context.trace.record(BindingContext.FUNCTION, expression, functionDescriptor)
} }
private fun bindPropertyReference(expression: KtCallableReferenceExpression, referenceType: KotlinType, context: ResolutionContext<*>) { internal fun bindPropertyReference(expression: KtCallableReferenceExpression, referenceType: KotlinType, context: ResolutionContext<*>) {
val localVariable = LocalVariableDescriptor(context.scope.ownerDescriptor, Annotations.EMPTY, Name.special("<anonymous>"), val localVariable = LocalVariableDescriptor(context.scope.ownerDescriptor, Annotations.EMPTY, Name.special("<anonymous>"),
referenceType, /* mutable = */ false, false, expression.toSourceElement()) referenceType, /* mutable = */ false, false, expression.toSourceElement())
context.trace.record(BindingContext.VARIABLE, expression, localVariable) context.trace.record(BindingContext.VARIABLE, expression, localVariable)
} }
private fun createReflectionTypeForCallableDescriptor( fun createReflectionTypeForCallableDescriptor(
descriptor: CallableDescriptor, descriptor: CallableDescriptor,
lhsType: KotlinType?, lhsType: KotlinType?,
reflectionTypes: ReflectionTypes, reflectionTypes: ReflectionTypes,
trace: BindingTrace?,
reportOn: KtExpression?,
ignoreReceiver: Boolean, ignoreReceiver: Boolean,
scopeOwnerDescriptor: DeclarationDescriptor scopeOwnerDescriptor: DeclarationDescriptor
): KotlinType? { ): KotlinType? {
if (descriptor is CallableMemberDescriptor && isMemberExtension(descriptor)) {
if (reportOn != null) {
trace?.report(EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED.on(reportOn, descriptor))
}
return null
}
val extensionReceiver = descriptor.extensionReceiverParameter val extensionReceiver = descriptor.extensionReceiverParameter
val dispatchReceiver = descriptor.dispatchReceiverParameter?.let { dispatchReceiver -> val dispatchReceiver = descriptor.dispatchReceiverParameter?.let { dispatchReceiver ->
// See CallableDescriptor#getOwnerForEffectiveDispatchReceiverParameter // See CallableDescriptor#getOwnerForEffectiveDispatchReceiverParameter
@@ -228,52 +221,11 @@ private fun createReflectionTypeForCallableDescriptor(
} }
reflectionTypes.getKPropertyType(Annotations.EMPTY, receiverType, descriptor.type, mutable) reflectionTypes.getKPropertyType(Annotations.EMPTY, receiverType, descriptor.type, mutable)
} }
is VariableDescriptor -> { is VariableDescriptor -> null
if (reportOn != null) { else -> throw UnsupportedOperationException("Callable reference resolved to an unsupported descriptor: $descriptor")
trace?.report(UNSUPPORTED.on(reportOn, "References to variables aren't supported yet"))
}
null
}
else ->
throw UnsupportedOperationException("Callable reference resolved to an unsupported descriptor: $descriptor")
} }
} }
private fun isMemberExtension(descriptor: CallableMemberDescriptor): Boolean {
val original = (descriptor as? ImportedFromObjectCallableDescriptor<*>)?.callableFromObject ?: descriptor
return original.extensionReceiverParameter != null && original.dispatchReceiverParameter != null
}
fun getReflectionTypeForCandidateDescriptor(
descriptor: CallableDescriptor,
reflectionTypes: ReflectionTypes,
ignoreReceiver: Boolean,
scopeOwnerDescriptor: DeclarationDescriptor
): KotlinType? =
createReflectionTypeForCallableDescriptor(descriptor, null, reflectionTypes, null, null, ignoreReceiver, scopeOwnerDescriptor)
fun createReflectionTypeForResolvedCallableReference(
reference: KtCallableReferenceExpression,
lhsType: KotlinType?,
ignoreReceiver: Boolean,
descriptor: CallableDescriptor,
context: ResolutionContext<*>,
reflectionTypes: ReflectionTypes
): KotlinType? {
val type = createReflectionTypeForCallableDescriptor(
descriptor, lhsType, reflectionTypes, context.trace, reference.callableReference, ignoreReceiver, context.scope.ownerDescriptor
) ?: return null
when (descriptor) {
is FunctionDescriptor -> {
bindFunctionReference(reference, type, context)
}
is PropertyDescriptor -> {
bindPropertyReference(reference, type, context)
}
}
return type
}
fun getResolvedCallableReferenceShapeType( fun getResolvedCallableReferenceShapeType(
reference: KtCallableReferenceExpression, reference: KtCallableReferenceExpression,
lhsType: KotlinType?, lhsType: KotlinType?,
@@ -290,8 +242,7 @@ fun getResolvedCallableReferenceShapeType(
overloadResolutionResults.isSingleResult -> overloadResolutionResults.isSingleResult ->
OverloadResolutionResultsUtil.getResultingCall(overloadResolutionResults, context.contextDependency)?.let { call -> OverloadResolutionResultsUtil.getResultingCall(overloadResolutionResults, context.contextDependency)?.let { call ->
createReflectionTypeForCallableDescriptor( createReflectionTypeForCallableDescriptor(
call.resultingDescriptor, lhsType, reflectionTypes, context.trace, reference, reference.isEmptyLHS, call.resultingDescriptor, lhsType, reflectionTypes, reference.isEmptyLHS, context.scope.ownerDescriptor
context.scope.ownerDescriptor
) )
} }
expectedTypeUnknown /* && overload resolution was ambiguous */ -> expectedTypeUnknown /* && overload resolution was ambiguous */ ->
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.callableReferences.getReflectionTypeForCandidateDescriptor import org.jetbrains.kotlin.resolve.callableReferences.createReflectionTypeForCallableDescriptor
import org.jetbrains.kotlin.resolve.calls.CallTransformer.CallForImplicitInvoke import org.jetbrains.kotlin.resolve.calls.CallTransformer.CallForImplicitInvoke
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS
@@ -158,18 +158,12 @@ class CandidateResolver(
private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkExpectedCallableType() private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkExpectedCallableType()
= check { = check {
if (!noExpectedType(expectedType)) { if (!noExpectedType(expectedType)) {
val candidate = candidateCall.candidateDescriptor val callableReferenceExpression = call.callElement.parent as? KtCallableReferenceExpression
val candidateReflectionType = getReflectionTypeForCandidateDescriptor( val candidateReflectionType = createReflectionTypeForCallableDescriptor(
candidate, reflectionTypes, candidateCall.candidateDescriptor, null, reflectionTypes,
call.callElement.parent.let { it is KtCallableReferenceExpression && it.isEmptyLHS }, callableReferenceExpression?.isEmptyLHS == true, scope.ownerDescriptor
scope.ownerDescriptor
) )
if (candidateReflectionType != null) { if (candidateReflectionType == null || !KotlinTypeChecker.DEFAULT.isSubtypeOf(candidateReflectionType, expectedType)) {
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(candidateReflectionType, expectedType)) {
candidateCall.addStatus(OTHER_ERROR)
}
}
else {
candidateCall.addStatus(OTHER_ERROR) candidateCall.addStatus(OTHER_ERROR)
} }
} }
@@ -20,17 +20,16 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageFeatureSettings import org.jetbrains.kotlin.config.LanguageFeatureSettings
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.callableReferences.createReflectionTypeForResolvedCallableReference import org.jetbrains.kotlin.resolve.callableReferences.bindFunctionReference
import org.jetbrains.kotlin.resolve.callableReferences.bindPropertyReference
import org.jetbrains.kotlin.resolve.callableReferences.createReflectionTypeForCallableDescriptor
import org.jetbrains.kotlin.resolve.callableReferences.resolvePossiblyAmbiguousCallableReference import org.jetbrains.kotlin.resolve.callableReferences.resolvePossiblyAmbiguousCallableReference
import org.jetbrains.kotlin.resolve.calls.CallResolver import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode
@@ -272,32 +271,53 @@ class DoubleColonExpressionResolver(
resolutionResults: OverloadResolutionResults<*>?, resolutionResults: OverloadResolutionResults<*>?,
context: ExpressionTypingContext context: ExpressionTypingContext
): KotlinType? { ): KotlinType? {
val reference = expression.callableReference
val descriptor = val descriptor =
if (resolutionResults != null && !resolutionResults.isNothing) { if (resolutionResults != null && !resolutionResults.isNothing) {
OverloadResolutionResultsUtil.getResultingCall(resolutionResults, context.contextDependency)?.let { call -> val resolvedCall = OverloadResolutionResultsUtil.getResultingCall(resolutionResults, context.contextDependency)
call.resultingDescriptor resolvedCall?.resultingDescriptor ?: return null
} ?: return null
} }
else { else {
context.trace.report(UNRESOLVED_REFERENCE.on(reference, reference)) context.trace.report(UNRESOLVED_REFERENCE.on(expression.callableReference, expression.callableReference))
return null return null
} }
if (expression.isEmptyLHS && checkReferenceIsToAllowedMember(descriptor, context.trace, expression)
(descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)) {
context.trace.report(CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS.on(reference))
}
if (descriptor is ConstructorDescriptor && DescriptorUtils.isAnnotationClass(descriptor.containingDeclaration)) {
context.trace.report(CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR.on(reference))
}
val ignoreReceiver = lhs is DoubleColonLHS.Expression || expression.isEmptyLHS val ignoreReceiver = lhs is DoubleColonLHS.Expression || expression.isEmptyLHS
return createReflectionTypeForResolvedCallableReference( val type = createReflectionTypeForCallableDescriptor(
expression, lhs?.type, ignoreReceiver, descriptor, context, reflectionTypes descriptor, lhs?.type, reflectionTypes, ignoreReceiver, context.scope.ownerDescriptor
) ) ?: return null
when (descriptor) {
is FunctionDescriptor -> bindFunctionReference(expression, type, context)
is PropertyDescriptor -> bindPropertyReference(expression, type, context)
}
return type
}
private fun checkReferenceIsToAllowedMember(
descriptor: CallableDescriptor, trace: BindingTrace, expression: KtCallableReferenceExpression
) {
val simpleName = expression.callableReference
if (expression.isEmptyLHS &&
(descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)) {
trace.report(CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS.on(simpleName))
}
if (descriptor is ConstructorDescriptor && DescriptorUtils.isAnnotationClass(descriptor.containingDeclaration)) {
trace.report(CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR.on(simpleName))
}
if (descriptor is CallableMemberDescriptor && isMemberExtension(descriptor)) {
trace.report(EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED.on(simpleName, descriptor))
}
if (descriptor is VariableDescriptor && descriptor !is PropertyDescriptor) {
trace.report(UNSUPPORTED.on(simpleName, "References to variables aren't supported yet"))
}
}
private fun isMemberExtension(descriptor: CallableMemberDescriptor): Boolean {
val original = (descriptor as? ImportedFromObjectCallableDescriptor<*>)?.callableFromObject ?: descriptor
return original.extensionReceiverParameter != null && original.dispatchReceiverParameter != null
} }
fun resolveCallableReference( fun resolveCallableReference(
@@ -1,4 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION
class A { class A {
fun Int.extInt() = 42 fun Int.extInt() = 42
fun A.extA(x: String) = x fun A.extA(x: String) = x
@@ -6,9 +6,14 @@ class A {
fun main() { fun main() {
Int::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extInt<!> Int::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extInt<!>
A::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extA<!> A::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extA<!>
eat(Int::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extInt<!>)
eat(A::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extA<!>)
} }
} }
fun eat(value: Any) {}
fun main() { fun main() {
A::<!UNRESOLVED_REFERENCE!>extInt<!> A::<!UNRESOLVED_REFERENCE!>extInt<!>
A::<!UNRESOLVED_REFERENCE!>extA<!> A::<!UNRESOLVED_REFERENCE!>extA<!>
@@ -1,5 +1,6 @@
package package
public fun eat(/*0*/ value: kotlin.Any): kotlin.Unit
public fun main(): kotlin.Unit public fun main(): kotlin.Unit
public final class A { public final class A {
@@ -1,5 +1,7 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
fun eat(value: Any) {}
fun test(param: String) { fun test(param: String) {
val a = ::<!UNSUPPORTED!>param<!> val a = ::<!UNSUPPORTED!>param<!>
@@ -8,4 +10,6 @@ fun test(param: String) {
val lambda = { -> } val lambda = { -> }
val g = ::<!UNSUPPORTED!>lambda<!> val g = ::<!UNSUPPORTED!>lambda<!>
eat(::<!UNSUPPORTED!>param<!>)
} }
@@ -1,3 +1,4 @@
package package
public fun eat(/*0*/ value: kotlin.Any): kotlin.Unit
public fun test(/*0*/ param: kotlin.String): kotlin.Unit public fun test(/*0*/ param: kotlin.String): kotlin.Unit
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.idea.completion.suppressAutoInsertion
import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.resolve.callableReferences.getReflectionTypeForCandidateDescriptor import org.jetbrains.kotlin.resolve.callableReferences.createReflectionTypeForCallableDescriptor
import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.TypeNullability import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isNothing
@@ -248,8 +248,8 @@ private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factor
fun CallableDescriptor.callableReferenceType(resolutionFacade: ResolutionFacade): FuzzyType? { fun CallableDescriptor.callableReferenceType(resolutionFacade: ResolutionFacade): FuzzyType? {
if (!CallType.CALLABLE_REFERENCE.descriptorKindFilter.accepts(this)) return null // not supported by callable references if (!CallType.CALLABLE_REFERENCE.descriptorKindFilter.accepts(this)) return null // not supported by callable references
return getReflectionTypeForCandidateDescriptor( return createReflectionTypeForCallableDescriptor(
this, resolutionFacade.getFrontendService(ReflectionTypes::class.java), false, resolutionFacade.moduleDescriptor this, null, resolutionFacade.getFrontendService(ReflectionTypes::class.java), false, resolutionFacade.moduleDescriptor
)?.toFuzzyType(emptyList()) )?.toFuzzyType(emptyList())
} }