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.impl.AnonymousFunctionDescriptor
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.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtExpression
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.callResolverUtil.ResolveArgumentsMode
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.OverloadResolutionResultsUtil
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.LexicalScopeKind
import org.jetbrains.kotlin.resolve.scopes.MemberScope
@@ -159,7 +161,7 @@ fun resolvePossiblyAmbiguousCallableReference(
return null
}
private fun bindFunctionReference(expression: KtCallableReferenceExpression, type: KotlinType, context: ResolutionContext<*>) {
internal fun bindFunctionReference(expression: KtCallableReferenceExpression, type: KotlinType, context: ResolutionContext<*>) {
val functionDescriptor = AnonymousFunctionDescriptor(
context.scope.ownerDescriptor,
Annotations.EMPTY,
@@ -178,29 +180,20 @@ private fun bindFunctionReference(expression: KtCallableReferenceExpression, typ
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>"),
referenceType, /* mutable = */ false, false, expression.toSourceElement())
context.trace.record(BindingContext.VARIABLE, expression, localVariable)
}
private fun createReflectionTypeForCallableDescriptor(
fun createReflectionTypeForCallableDescriptor(
descriptor: CallableDescriptor,
lhsType: KotlinType?,
reflectionTypes: ReflectionTypes,
trace: BindingTrace?,
reportOn: KtExpression?,
ignoreReceiver: Boolean,
scopeOwnerDescriptor: DeclarationDescriptor
): 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 dispatchReceiver = descriptor.dispatchReceiverParameter?.let { dispatchReceiver ->
// See CallableDescriptor#getOwnerForEffectiveDispatchReceiverParameter
@@ -228,52 +221,11 @@ private fun createReflectionTypeForCallableDescriptor(
}
reflectionTypes.getKPropertyType(Annotations.EMPTY, receiverType, descriptor.type, mutable)
}
is VariableDescriptor -> {
if (reportOn != null) {
trace?.report(UNSUPPORTED.on(reportOn, "References to variables aren't supported yet"))
}
null
}
else ->
throw UnsupportedOperationException("Callable reference resolved to an unsupported descriptor: $descriptor")
is VariableDescriptor -> 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(
reference: KtCallableReferenceExpression,
lhsType: KotlinType?,
@@ -290,8 +242,7 @@ fun getResolvedCallableReferenceShapeType(
overloadResolutionResults.isSingleResult ->
OverloadResolutionResultsUtil.getResultingCall(overloadResolutionResults, context.contextDependency)?.let { call ->
createReflectionTypeForCallableDescriptor(
call.resultingDescriptor, lhsType, reflectionTypes, context.trace, reference, reference.isEmptyLHS,
context.scope.ownerDescriptor
call.resultingDescriptor, lhsType, reflectionTypes, reference.isEmptyLHS, context.scope.ownerDescriptor
)
}
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.psi.*
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.callResolverUtil.ResolveArgumentsMode
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS
@@ -158,18 +158,12 @@ class CandidateResolver(
private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkExpectedCallableType()
= check {
if (!noExpectedType(expectedType)) {
val candidate = candidateCall.candidateDescriptor
val candidateReflectionType = getReflectionTypeForCandidateDescriptor(
candidate, reflectionTypes,
call.callElement.parent.let { it is KtCallableReferenceExpression && it.isEmptyLHS },
scope.ownerDescriptor
val callableReferenceExpression = call.callElement.parent as? KtCallableReferenceExpression
val candidateReflectionType = createReflectionTypeForCallableDescriptor(
candidateCall.candidateDescriptor, null, reflectionTypes,
callableReferenceExpression?.isEmptyLHS == true, scope.ownerDescriptor
)
if (candidateReflectionType != null) {
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(candidateReflectionType, expectedType)) {
candidateCall.addStatus(OTHER_ERROR)
}
}
else {
if (candidateReflectionType == null || !KotlinTypeChecker.DEFAULT.isSubtypeOf(candidateReflectionType, expectedType)) {
candidateCall.addStatus(OTHER_ERROR)
}
}
@@ -20,17 +20,16 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageFeatureSettings
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
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.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode
@@ -272,32 +271,53 @@ class DoubleColonExpressionResolver(
resolutionResults: OverloadResolutionResults<*>?,
context: ExpressionTypingContext
): KotlinType? {
val reference = expression.callableReference
val descriptor =
if (resolutionResults != null && !resolutionResults.isNothing) {
OverloadResolutionResultsUtil.getResultingCall(resolutionResults, context.contextDependency)?.let { call ->
call.resultingDescriptor
} ?: return null
val resolvedCall = OverloadResolutionResultsUtil.getResultingCall(resolutionResults, context.contextDependency)
resolvedCall?.resultingDescriptor ?: return null
}
else {
context.trace.report(UNRESOLVED_REFERENCE.on(reference, reference))
context.trace.report(UNRESOLVED_REFERENCE.on(expression.callableReference, expression.callableReference))
return null
}
if (expression.isEmptyLHS &&
(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))
}
checkReferenceIsToAllowedMember(descriptor, context.trace, expression)
val ignoreReceiver = lhs is DoubleColonLHS.Expression || expression.isEmptyLHS
return createReflectionTypeForResolvedCallableReference(
expression, lhs?.type, ignoreReceiver, descriptor, context, reflectionTypes
)
val type = createReflectionTypeForCallableDescriptor(
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(
@@ -1,4 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION
class A {
fun Int.extInt() = 42
fun A.extA(x: String) = x
@@ -6,9 +6,14 @@ class A {
fun main() {
Int::<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>extInt<!>
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() {
A::<!UNRESOLVED_REFERENCE!>extInt<!>
A::<!UNRESOLVED_REFERENCE!>extA<!>
@@ -1,5 +1,6 @@
package
public fun eat(/*0*/ value: kotlin.Any): kotlin.Unit
public fun main(): kotlin.Unit
public final class A {
@@ -1,5 +1,7 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
fun eat(value: Any) {}
fun test(param: String) {
val a = ::<!UNSUPPORTED!>param<!>
@@ -8,4 +10,6 @@ fun test(param: String) {
val lambda = { -> }
val g = ::<!UNSUPPORTED!>lambda<!>
eat(::<!UNSUPPORTED!>param<!>)
}
@@ -1,3 +1,4 @@
package
public fun eat(/*0*/ value: kotlin.Any): 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.resolve.ResolutionFacade
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.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.isNothing
@@ -248,8 +248,8 @@ private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factor
fun CallableDescriptor.callableReferenceType(resolutionFacade: ResolutionFacade): FuzzyType? {
if (!CallType.CALLABLE_REFERENCE.descriptorKindFilter.accepts(this)) return null // not supported by callable references
return getReflectionTypeForCandidateDescriptor(
this, resolutionFacade.getFrontendService(ReflectionTypes::class.java), false, resolutionFacade.moduleDescriptor
return createReflectionTypeForCallableDescriptor(
this, null, resolutionFacade.getFrontendService(ReflectionTypes::class.java), false, resolutionFacade.moduleDescriptor
)?.toFuzzyType(emptyList())
}