From 80bf6e1beeb17f5df5096710b583d4d90d7b9c09 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 21 Jun 2016 17:32:29 +0300 Subject: [PATCH] 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 --- .../CallableReferencesResolutionUtils.kt | 71 +++---------------- .../kotlin/resolve/calls/CandidateResolver.kt | 18 ++--- .../DoubleColonExpressionResolver.kt | 64 +++++++++++------ .../function/extensionInClassDisallowed.kt | 7 +- .../function/extensionInClassDisallowed.txt | 1 + .../property/localVariable.kt | 4 ++ .../property/localVariable.txt | 1 + .../kotlin/idea/completion/smart/Utils.kt | 6 +- 8 files changed, 74 insertions(+), 98 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt index a966b099e77..65ed7b93eb9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt @@ -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(""), 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 */ -> diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index 60ebad81fe5..ec941675074 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -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 CallCandidateResolutionContext.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) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index b541428aa18..c1e911a945f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -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( diff --git a/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.kt b/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.kt index e676cb8a978..680fec1fb6b 100644 --- a/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.kt +++ b/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.kt @@ -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::extInt A::extA + + eat(Int::extInt) + eat(A::extA) } } +fun eat(value: Any) {} + fun main() { A::extInt A::extA diff --git a/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.txt b/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.txt index 23e83686c9a..8f8cb02026a 100644 --- a/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.txt +++ b/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.txt @@ -1,5 +1,6 @@ package +public fun eat(/*0*/ value: kotlin.Any): kotlin.Unit public fun main(): kotlin.Unit public final class A { diff --git a/compiler/testData/diagnostics/tests/callableReference/property/localVariable.kt b/compiler/testData/diagnostics/tests/callableReference/property/localVariable.kt index 5550b022d63..3639382638e 100644 --- a/compiler/testData/diagnostics/tests/callableReference/property/localVariable.kt +++ b/compiler/testData/diagnostics/tests/callableReference/property/localVariable.kt @@ -1,5 +1,7 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE +fun eat(value: Any) {} + fun test(param: String) { val a = ::param @@ -8,4 +10,6 @@ fun test(param: String) { val lambda = { -> } val g = ::lambda + + eat(::param) } diff --git a/compiler/testData/diagnostics/tests/callableReference/property/localVariable.txt b/compiler/testData/diagnostics/tests/callableReference/property/localVariable.txt index a9a1817783f..61a12dedf90 100644 --- a/compiler/testData/diagnostics/tests/callableReference/property/localVariable.txt +++ b/compiler/testData/diagnostics/tests/callableReference/property/localVariable.txt @@ -1,3 +1,4 @@ package +public fun eat(/*0*/ value: kotlin.Any): kotlin.Unit public fun test(/*0*/ param: kotlin.String): kotlin.Unit diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index 454561a2172..d546ff9fcd8 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -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.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()) }