Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.types.expressions
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
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.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
import org.jetbrains.kotlin.psi.psiUtil.checkReservedYield
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.context.*
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.calls.util.createValueParametersForInvokeInFunctionType
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.kotlin.resolve.source.toSourceElement
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlin.utils.yieldIfNotNull
import java.lang.UnsupportedOperationException
import java.util.*
import javax.inject.Inject
import kotlin.coroutines.experimental.buildSequence
sealed class DoubleColonLHS(val type: KotlinType) {
/**
* [isObjectQualifier] is true iff the LHS of a callable reference is a qualified expression which references a named object.
* Note that such LHS can be treated both as a type and as an expression, so special handling may be required.
*
* For example, if `Obj` is an object:
*
* Obj::class // object qualifier
* test.Obj::class // object qualifier
* (Obj)::class // not an object qualifier (can only be treated as an expression, not as a type)
* { Obj }()::class // not an object qualifier
*/
class Expression(typeInfo: KotlinTypeInfo, val isObjectQualifier: Boolean) : DoubleColonLHS(typeInfo.type!!) {
val dataFlowInfo: DataFlowInfo = typeInfo.dataFlowInfo
}
class Type(type: KotlinType, val possiblyBareType: PossiblyBareType) : DoubleColonLHS(type)
}
// Returns true if this expression has the form "A" which means it's a type on the LHS of a double colon expression
internal val KtCallExpression.isWithoutValueArguments: Boolean
get() = valueArgumentList == null && lambdaArguments.isEmpty()
class DoubleColonExpressionResolver(
val callResolver: CallResolver,
val qualifiedExpressionResolver: QualifiedExpressionResolver,
val dataFlowAnalyzer: DataFlowAnalyzer,
val reflectionTypes: ReflectionTypes,
val typeResolver: TypeResolver,
val languageVersionSettings: LanguageVersionSettings,
val additionalCheckers: Iterable
) {
private lateinit var expressionTypingServices: ExpressionTypingServices
// component dependency cycle
@Inject
fun setExpressionTypingServices(expressionTypingServices: ExpressionTypingServices) {
this.expressionTypingServices = expressionTypingServices
}
fun visitClassLiteralExpression(expression: KtClassLiteralExpression, c: ExpressionTypingContext): KotlinTypeInfo {
if (expression.isEmptyLHS) {
// "::class" will maybe mean "this::class", a class of "this" instance
c.trace.report(UNSUPPORTED.on(expression, "Class literals with empty left hand side are not yet supported"))
}
else {
val result = resolveDoubleColonLHS(expression, c)
if (result != null && !result.type.isError) {
val inherentType = result.type
val dataFlowInfo = (result as? DoubleColonLHS.Expression)?.dataFlowInfo ?: c.dataFlowInfo
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression.receiverExpression!!, inherentType, c)
val type =
if (!dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()) inherentType.makeNotNullable()
else inherentType
checkClassLiteral(c, expression, type, result)
val variance =
if (result is DoubleColonLHS.Expression && !result.isObjectQualifier) Variance.OUT_VARIANCE else Variance.INVARIANT
val kClassType = reflectionTypes.getKClassType(Annotations.EMPTY, type, variance)
return dataFlowAnalyzer.checkType(createTypeInfo(kClassType, dataFlowInfo), expression, c)
}
}
return createTypeInfo(ErrorUtils.createErrorType("Unresolved class"), c)
}
private fun checkClassLiteral(
c: ExpressionTypingContext,
expression: KtClassLiteralExpression,
type: KotlinType,
result: DoubleColonLHS
) {
if (result is DoubleColonLHS.Expression) {
if (!result.isObjectQualifier) {
if (!type.isSubtypeOf(type.builtIns.anyType)) {
c.trace.report(EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS.on(expression.receiverExpression!!, type))
}
reportUnsupportedIfNeeded(expression, c)
}
return
}
result as DoubleColonLHS.Type
val descriptor = type.constructor.declarationDescriptor
if (result.possiblyBareType.isBare) {
if (descriptor is ClassDescriptor && KotlinBuiltIns.isNonPrimitiveArray(descriptor)) {
c.trace.report(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT.on(expression))
}
}
if (type is SimpleType && !type.isMarkedNullable && descriptor is TypeParameterDescriptor && !descriptor.isReified) {
c.trace.report(TYPE_PARAMETER_AS_REIFIED.on(expression, descriptor))
}
// Note that "T::class" is allowed for type parameter T without a non-null upper bound
else if ((TypeUtils.isNullableType(type) && descriptor !is TypeParameterDescriptor) || expression.hasQuestionMarks) {
c.trace.report(NULLABLE_TYPE_IN_CLASS_LITERAL_LHS.on(expression))
}
else if (!result.possiblyBareType.isBare && !isAllowedInClassLiteral(type)) {
c.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression))
}
for (additionalChecker in additionalCheckers) {
additionalChecker.check(expression, type, c)
}
}
// Returns true if the expression is not a call expression without value arguments (such as "A") or a qualified expression
// which contains such call expression as one of its parts.
// In this case it's pointless to attempt to type check an expression on the LHS in "A::class", since "A" certainly means a type.
private fun KtExpression.canBeConsideredProperExpression(): Boolean {
return when (this) {
is KtCallExpression ->
!isWithoutValueArguments
is KtDotQualifiedExpression ->
receiverExpression.canBeConsideredProperExpression() &&
selectorExpression?.canBeConsideredProperExpression() ?: false
else -> true
}
}
private fun KtExpression.canBeConsideredProperType(): Boolean {
return when (this) {
is KtSimpleNameExpression ->
true
is KtCallExpression ->
isWithoutValueArguments
is KtDotQualifiedExpression ->
receiverExpression.canBeConsideredProperType() && selectorExpression.let { it != null && it.canBeConsideredProperType() }
else -> false
}
}
private fun shouldTryResolveLHSAsExpression(expression: KtDoubleColonExpression): Boolean {
val lhs = expression.receiverExpression ?: return false
return lhs.canBeConsideredProperExpression() && !expression.hasQuestionMarks /* TODO: test this */
}
private fun shouldTryResolveLHSAsType(expression: KtDoubleColonExpression): Boolean {
val lhs = expression.receiverExpression
return lhs != null && lhs.canBeConsideredProperType()
}
private fun reportUnsupportedIfNeeded(expression: KtDoubleColonExpression, c: ExpressionTypingContext) {
if (!languageVersionSettings.supportsFeature(LanguageFeature.BoundCallableReferences)) {
c.trace.report(UNSUPPORTED_FEATURE.on(expression.receiverExpression!!, LanguageFeature.BoundCallableReferences to languageVersionSettings))
}
}
private fun shouldTryResolveLHSAsReservedExpression(expression: KtDoubleColonExpression): Boolean {
val lhs = expression.receiverExpression ?: return false
return (expression.hasQuestionMarks && lhs.canBeConsideredProperExpression()) ||
(lhs is KtCallExpression && lhs.canBeReservedGenericPropertyCall())
}
private fun KtExpression.getQualifierChainParts(): List? {
if (this !is KtQualifiedExpression) return listOf(this)
val result = ArrayDeque()
var finger: KtQualifiedExpression = this
while (true) {
if (finger.operationSign != KtTokens.DOT) return null
finger.selectorExpression?.let { result.push(it) }
val receiver = finger.receiverExpression
if (receiver is KtQualifiedExpression) {
finger = receiver
}
else {
result.push(receiver)
return result.toList()
}
}
}
private fun shouldTryResolveLHSAsReservedCallChain(expression: KtDoubleColonExpression): Boolean {
val lhs = (expression.receiverExpression as? KtQualifiedExpression) ?: return false
val parts = lhs.getQualifierChainParts() ?: return false
return parts.all { it.canBeReservedGenericPropertyCall() } &&
parts.any { it is KtCallExpression && it.typeArguments.isNotEmpty() }
}
private fun KtExpression?.canBeReservedGenericPropertyCall(): Boolean =
getQualifiedNameStringPart() != null
private fun KtExpression?.getQualifiedNameStringPart(): String? =
when (this) {
is KtNameReferenceExpression ->
text
is KtCallExpression ->
if (valueArguments.isEmpty() && typeArguments.isNotEmpty())
(calleeExpression as? KtNameReferenceExpression)?.text
else
null
else ->
null
}
private fun KtQualifiedExpression.buildNewExpressionForReservedGenericPropertyCallChainResolution(): KtExpression? {
val parts = this.getQualifierChainParts()?.map { it.getQualifiedNameStringPart() ?: return null } ?: return null
val qualifiedExpressionText = parts.joinToString(separator = ".")
return KtPsiFactory(this, markGenerated = false).createExpression(qualifiedExpressionText)
}
private fun resolveReservedExpressionOnLHS(expression: KtExpression, c: ExpressionTypingContext): DoubleColonLHS.Expression? {
val doubleColonExpression = expression.parent as? KtDoubleColonExpression ?:
return null // should assert here?
if (expression is KtCallExpression && expression.typeArguments.isNotEmpty()) {
val callee = expression.calleeExpression ?: return null
val calleeAsDoubleColonLHS = resolveExpressionOnLHS(callee, c) ?: return null
for (typeArgument in expression.typeArguments) {
val typeReference = typeArgument.typeReference ?: continue
typeResolver.resolveType(c.scope, typeReference, c.trace, true)
}
return calleeAsDoubleColonLHS
}
else if (doubleColonExpression.hasQuestionMarks) {
return resolveExpressionOnLHS(expression, c)
}
else {
return null
}
}
private fun resolveReservedCallChainOnLHS(expression: KtExpression, c: ExpressionTypingContext): DoubleColonLHS.Expression? {
if (expression !is KtQualifiedExpression) return null
val newExpression = expression.buildNewExpressionForReservedGenericPropertyCallChainResolution() ?: return null
val temporaryTraceAndCache = TemporaryTraceAndCache.create(c, "resolve reserved generic property call chain in '::' LHS", newExpression)
val contextForCallChainResolution =
c.replaceTraceAndCache(temporaryTraceAndCache)
.replaceExpectedType(NO_EXPECTED_TYPE)
.replaceContextDependency(ContextDependency.INDEPENDENT)
return resolveExpressionOnLHS(expression, contextForCallChainResolution)
}
private fun resolveReservedExpressionSyntaxOnDoubleColonLHS(doubleColonExpression: KtDoubleColonExpression, c: ExpressionTypingContext):
Pair {
val resultForReservedExpr = tryResolveLHS(doubleColonExpression, c,
this::shouldTryResolveLHSAsReservedExpression,
this::resolveReservedExpressionOnLHS)
if (resultForReservedExpr != null) {
val lhs = resultForReservedExpr.lhs
if (lhs != null) {
c.trace.report(RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS.on(resultForReservedExpr.expression))
return Pair(true, resultForReservedExpr.commit())
}
}
val resultForReservedCallChain = tryResolveLHS(doubleColonExpression, c,
this::shouldTryResolveLHSAsReservedCallChain,
this::resolveReservedCallChainOnLHS)
if (resultForReservedCallChain != null) {
val lhs = resultForReservedCallChain.lhs
if (lhs != null) {
c.trace.report(RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS.on(resultForReservedCallChain.expression))
// DO NOT commit trace from resultForReservedCallChain here
return Pair(true, null)
}
}
return Pair(false, null)
}
private fun resolveDoubleColonLHS(doubleColonExpression: KtDoubleColonExpression, c: ExpressionTypingContext): DoubleColonLHS? {
val resultForExpr = tryResolveLHS(doubleColonExpression, c, this::shouldTryResolveLHSAsExpression, this::resolveExpressionOnLHS)
if (resultForExpr != null) {
val lhs = resultForExpr.lhs
// If expression result is an object, we remember this and skip it here, because there are valid situations where
// another type (representing another classifier) should win
if (lhs != null && !lhs.isObjectQualifier) {
return resultForExpr.commit()
}
}
val (isReservedExpressionSyntax, doubleColonLHS) = resolveReservedExpressionSyntaxOnDoubleColonLHS(doubleColonExpression, c)
if (isReservedExpressionSyntax) return doubleColonLHS
val resultForType = tryResolveLHS(doubleColonExpression, c, this::shouldTryResolveLHSAsType) { expression, context ->
resolveTypeOnLHS(expression, doubleColonExpression, context)
}
if (resultForType != null) {
val lhs = resultForType.lhs
if (resultForExpr != null && lhs != null && lhs.type == resultForExpr.lhs?.type) {
// If we skipped an object expression result before and the type result is the same, this means that
// there were no other classifier except that object that could win. We prefer to treat the LHS as an expression here,
// to have a bound callable reference / class literal
return resultForExpr.commit()
}
if (lhs != null) {
return resultForType.commit()
}
}
// If the LHS could be resolved neither as an expression nor as a type, we should still type-check it to allow all diagnostics
// to be reported and references to be resolved. For that, we commit one of the applicable traces here, preferring the expression
if (resultForExpr != null) return resultForExpr.commit()
if (resultForType != null) return resultForType.commit()
return null
}
private class LHSResolutionResult(
val lhs: T?,
val expression: KtExpression,
val traceAndCache: TemporaryTraceAndCache
) {
fun commit(): T? {
if (lhs != null) {
traceAndCache.trace.record(BindingContext.DOUBLE_COLON_LHS, expression, lhs)
}
traceAndCache.commit()
return lhs
}
}
/**
* Returns null if the LHS is definitely not an expression. Returns a non-null result if a resolution was attempted and led to
* either a successful result or not.
*/
private fun tryResolveLHS(
doubleColonExpression: KtDoubleColonExpression,
context: ExpressionTypingContext,
criterion: (KtDoubleColonExpression) -> Boolean,
resolve: (KtExpression, ExpressionTypingContext) -> T?
): LHSResolutionResult? {
val expression = doubleColonExpression.receiverExpression ?: return null
if (!criterion(doubleColonExpression)) return null
val traceAndCache = TemporaryTraceAndCache.create(context, "resolve '::' LHS", doubleColonExpression)
val c = context
.replaceTraceAndCache(traceAndCache)
.replaceExpectedType(NO_EXPECTED_TYPE)
.replaceContextDependency(ContextDependency.INDEPENDENT)
val lhs = resolve(expression, c)
return LHSResolutionResult(lhs, expression, traceAndCache)
}
private fun resolveExpressionOnLHS(expression: KtExpression, c: ExpressionTypingContext): DoubleColonLHS.Expression? {
val typeInfo = expressionTypingServices.getTypeInfo(expression, c)
// TODO: do not lose data flow info maybe
if (typeInfo.type == null) return null
// Be careful not to call a utility function to get a resolved call by an expression which may accidentally
// deparenthesize that expression, as this is undesirable here
val call = c.trace.bindingContext[BindingContext.CALL, expression.getQualifiedElementSelector()]
val resolvedCall = call.getResolvedCall(c.trace.bindingContext)
if (resolvedCall != null) {
val resultingDescriptor = resolvedCall.resultingDescriptor
if (resultingDescriptor is FakeCallableDescriptorForObject) {
val classDescriptor = resultingDescriptor.classDescriptor
if (classDescriptor.companionObjectDescriptor != null) return null
if (DescriptorUtils.isObject(classDescriptor) ||
(!languageVersionSettings.supportsFeature(LanguageFeature.BoundCallableReferences) &&
DescriptorUtils.isEnumEntry(classDescriptor))) {
return DoubleColonLHS.Expression(typeInfo, isObjectQualifier = true)
}
}
// Check if this is resolved to a function (with the error "arguments expected"), such as in "Runnable::class"
if (expression.canBeConsideredProperType() && resultingDescriptor !is VariableDescriptor) return null
}
return DoubleColonLHS.Expression(typeInfo, isObjectQualifier = false)
}
private fun resolveTypeOnLHS(
expression: KtExpression, doubleColonExpression: KtDoubleColonExpression, c: ExpressionTypingContext
): DoubleColonLHS.Type? {
val qualifierResolutionResult =
qualifiedExpressionResolver.resolveDescriptorForDoubleColonLHS(expression, c.scope, c.trace, c.isDebuggerContext)
val typeResolutionContext = TypeResolutionContext(
c.scope, c.trace, /* checkBounds = */ true, /* allowBareTypes = */ true,
/* isDebuggerContext = */ expression.suppressDiagnosticsInDebugMode() /* TODO: test this */
)
val classifier = qualifierResolutionResult.classifierDescriptor
if (classifier == null) {
typeResolver.resolveTypeProjections(
typeResolutionContext, ErrorUtils.createErrorType("No type").constructor, qualifierResolutionResult.allProjections
)
return null
}
val possiblyBareType = typeResolver.resolveTypeForClassifier(
typeResolutionContext, classifier, qualifierResolutionResult, expression, Annotations.EMPTY
)
val type = if (possiblyBareType.isBare) {
val descriptor = possiblyBareType.bareTypeConstructor.declarationDescriptor as? ClassDescriptor
?: error("Only classes can produce bare types: $possiblyBareType")
if (doubleColonExpression is KtCallableReferenceExpression) {
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(expression, descriptor.typeConstructor.parameters.size, descriptor))
}
val arguments = descriptor.typeConstructor.parameters.map(TypeUtils::makeStarProjection)
KotlinTypeFactory.simpleType(
Annotations.EMPTY, descriptor.typeConstructor, arguments,
possiblyBareType.isNullable || doubleColonExpression.hasQuestionMarks
)
}
else {
val actualType = possiblyBareType.actualType
if (doubleColonExpression.hasQuestionMarks) actualType.makeNullable() else actualType
}
return DoubleColonLHS.Type(type, possiblyBareType)
}
private fun isAllowedInClassLiteral(type: KotlinType): Boolean {
val typeConstructor = type.constructor
val descriptor = typeConstructor.declarationDescriptor
when (descriptor) {
is ClassDescriptor -> {
if (KotlinBuiltIns.isNonPrimitiveArray(descriptor)) {
return type.arguments.none { typeArgument ->
typeArgument.isStarProjection || !isAllowedInClassLiteral(typeArgument.type)
}
}
return type.arguments.isEmpty()
}
is TypeParameterDescriptor -> return descriptor.isReified
else -> return false
}
}
fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression, c: ExpressionTypingContext): KotlinTypeInfo {
val callableReference = expression.callableReference
if (callableReference.getReferencedName().isEmpty()) {
if (!expression.isEmptyLHS) resolveDoubleColonLHS(expression, c)
c.trace.report(UNRESOLVED_REFERENCE.on(callableReference, callableReference))
val errorType = ErrorUtils.createErrorType("Empty callable reference")
return dataFlowAnalyzer.createCheckedTypeInfo(errorType, c, expression)
}
val (lhs, resolutionResults) = resolveCallableReference(expression, c, ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS)
val result = getCallableReferenceType(expression, lhs, resolutionResults, c)
val dataFlowInfo = (lhs as? DoubleColonLHS.Expression)?.dataFlowInfo ?: c.dataFlowInfo
return dataFlowAnalyzer.checkType(createTypeInfo(result, dataFlowInfo), expression, c)
}
private fun getCallableReferenceType(
expression: KtCallableReferenceExpression,
lhs: DoubleColonLHS?,
resolutionResults: OverloadResolutionResults<*>?,
context: ExpressionTypingContext
): KotlinType? {
val descriptor =
if (resolutionResults != null && !resolutionResults.isNothing) {
val resolvedCall = OverloadResolutionResultsUtil.getResultingCall(resolutionResults, context.contextDependency)
resolvedCall?.resultingDescriptor ?: return null
}
else {
if (lhs != null || expression.isEmptyLHS) {
context.trace.report(UNRESOLVED_REFERENCE.on(expression.callableReference, expression.callableReference))
}
return null
}
checkReferenceIsToAllowedMember(descriptor, context.trace, expression)
val type = createKCallableTypeForReference(descriptor, lhs, reflectionTypes, 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
}
private fun bindFunctionReference(expression: KtCallableReferenceExpression, type: KotlinType, context: ResolutionContext<*>) {
val functionDescriptor = AnonymousFunctionDescriptor(
context.scope.ownerDescriptor,
Annotations.EMPTY,
CallableMemberDescriptor.Kind.DECLARATION,
expression.toSourceElement(),
/* isCoroutine = */ false
)
functionDescriptor.initialize(
null, null, emptyList(),
createValueParametersForInvokeInFunctionType(functionDescriptor, type.arguments.dropLast(1)),
type.arguments.last().type,
Modality.FINAL,
Visibilities.PUBLIC
)
context.trace.record(BindingContext.FUNCTION, expression, functionDescriptor)
}
private fun bindPropertyReference(expression: KtCallableReferenceExpression, referenceType: KotlinType, context: ResolutionContext<*>) {
val localVariable = LocalVariableDescriptor(
context.scope.ownerDescriptor, Annotations.EMPTY, Name.special(""), referenceType, /* mutable = */ false,
/* isDelegated = */ false, expression.toSourceElement()
)
context.trace.record(BindingContext.VARIABLE, expression, localVariable)
}
fun resolveCallableReference(
expression: KtCallableReferenceExpression,
context: ExpressionTypingContext,
resolveArgumentsMode: ResolveArgumentsMode
): Pair?> {
val lhsResult =
if (expression.isEmptyLHS) null
else resolveDoubleColonLHS(expression, context)
val resolutionResults = resolveCallableReferenceRHS(expression, lhsResult, context, resolveArgumentsMode)
reportUnsupportedCallableReferenceIfNeeded(expression, context, lhsResult, resolutionResults)
return lhsResult to resolutionResults
}
private fun reportUnsupportedCallableReferenceIfNeeded(
expression: KtCallableReferenceExpression,
context: ExpressionTypingContext,
lhsResult: DoubleColonLHS?,
resolutionResults: OverloadResolutionResults?
) {
val descriptor =
if (resolutionResults?.isSingleResult == true) resolutionResults.resultingDescriptor as? FunctionDescriptor else null
if (descriptor?.isSuspend == true) {
context.trace.report(UNSUPPORTED.on(expression.callableReference, "Callable references to suspend functions"))
}
val expressionResult = lhsResult as? DoubleColonLHS.Expression ?: return
// "::foo" was not supported without bound callable references, except the case of a nested class constructor in an object
if (!expressionResult.isObjectQualifier || descriptor !is ConstructorDescriptor) {
reportUnsupportedIfNeeded(expression, context)
}
}
private class ResolutionResultsAndTraceCommitCallback(
val results: OverloadResolutionResults,
val commitTrace: () -> Unit
)
private fun tryResolveRHSWithReceiver(
traceTitle: String,
receiver: Receiver?,
reference: KtSimpleNameExpression,
outerContext: ResolutionContext<*>,
resolutionMode: ResolveArgumentsMode
): ResolutionResultsAndTraceCommitCallback? {
// we should preserve information about `call` because callable references are analyzed two times,
// otherwise there will be not completed calls in trace
val call = outerContext.trace[BindingContext.CALL, reference] ?: CallMaker.makeCall(reference, receiver, null, reference, emptyList())
val temporaryTrace = TemporaryTraceAndCache.create(outerContext, traceTitle, reference)
val newContext =
if (resolutionMode == ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS)
outerContext
.replaceTraceAndCache(temporaryTrace)
.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE)
.replaceContextDependency(ContextDependency.DEPENDENT)
else
outerContext.replaceTraceAndCache(temporaryTrace)
val resolutionResults = callResolver.resolveCallForMember(
reference, BasicCallResolutionContext.create(newContext, call, CheckArgumentTypesMode.CHECK_CALLABLE_TYPE)
)
return when {
resolutionResults.isNothing -> null
else -> ResolutionResultsAndTraceCommitCallback(resolutionResults) {
checkReservedYield(reference, outerContext.trace)
if (resolutionMode != ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS || resolutionResults.isSuccess) {
temporaryTrace.commit()
}
}
}
}
private fun resolveCallableReferenceRHS(
expression: KtCallableReferenceExpression,
lhs: DoubleColonLHS?,
c: ResolutionContext<*>,
mode: ResolveArgumentsMode
): OverloadResolutionResults? {
val reference = expression.callableReference
val lhsType = lhs?.type
if (lhsType == null) {
if (!expression.isEmptyLHS) return null
return tryResolveRHSWithReceiver("resolve callable reference with empty LHS", null, reference, c, mode)
?.apply { commitTrace() }?.results
}
val resultSequence = buildSequence {
when (lhs) {
is DoubleColonLHS.Type -> {
val classifier = lhsType.constructor.declarationDescriptor
if (classifier !is ClassDescriptor) {
c.trace.report(CALLABLE_REFERENCE_LHS_NOT_A_CLASS.on(expression))
return@buildSequence
}
val qualifier = c.trace.get(BindingContext.QUALIFIER, expression.receiverExpression!!)
if (qualifier is ClassQualifier) {
yieldIfNotNull(
tryResolveRHSWithReceiver(
"resolve unbound callable reference in static scope", qualifier, reference, c, mode
)
)
}
yieldIfNotNull(
tryResolveRHSWithReceiver(
"resolve unbound callable reference with receiver", TransientReceiver(lhsType), reference, c, mode
)
)
}
is DoubleColonLHS.Expression -> {
val expressionReceiver = ExpressionReceiver.create(expression.receiverExpression!!, lhsType, c.trace.bindingContext)
yieldIfNotNull(
tryResolveRHSWithReceiver(
"resolve bound callable reference", expressionReceiver, reference, c, mode
)
)
if (lhs.isObjectQualifier) {
val classifier = lhsType.constructor.declarationDescriptor
val calleeExpression = expression.receiverExpression?.getCalleeExpressionIfAny()
if (calleeExpression is KtSimpleNameExpression && classifier is ClassDescriptor) {
val qualifier = ClassQualifier(calleeExpression, classifier)
yieldIfNotNull(
tryResolveRHSWithReceiver(
"resolve object callable reference in static scope", qualifier, reference, c, mode
)
)
}
}
}
}
}
// TODO: Maybe it makes sense to report all results when all of them are unsuccessful (NONE_APPLICABLE or something like this)
var resultToCommit: ResolutionResultsAndTraceCommitCallback? = null
for (result in resultSequence) {
resultToCommit = result
if (result.results.isSuccess) {
break
}
}
return resultToCommit?.let {
it.commitTrace()
it.results
}
}
companion object {
fun createKCallableTypeForReference(
descriptor: CallableDescriptor,
lhs: DoubleColonLHS?,
reflectionTypes: ReflectionTypes,
scopeOwnerDescriptor: DeclarationDescriptor
): KotlinType? {
val receiverType =
if (descriptor.extensionReceiverParameter != null || descriptor.dispatchReceiverParameter != null)
(lhs as? DoubleColonLHS.Type)?.type
else null
return when (descriptor) {
is FunctionDescriptor -> {
val returnType = descriptor.returnType ?: return null
val parametersTypes = descriptor.valueParameters.map { it.type }
val parametersNames = descriptor.valueParameters.map { it.name }
return reflectionTypes.getKFunctionType(Annotations.EMPTY, receiverType,
parametersTypes, parametersNames, returnType, descriptor.builtIns)
}
is PropertyDescriptor -> {
val mutable = descriptor.isVar && run {
val setter = descriptor.setter
setter == null || Visibilities.isVisible(receiverType?.let(::TransientReceiver), setter, scopeOwnerDescriptor)
}
reflectionTypes.getKPropertyType(Annotations.EMPTY, listOfNotNull(receiverType), descriptor.type, mutable)
}
is VariableDescriptor -> null
else -> throw UnsupportedOperationException("Callable reference resolved to an unsupported descriptor: $descriptor")
}
}
}
}