[NI] Add pre-resolution callable reference argument check.

If expected type for callable reference argument isn't callable type
then make such candidate unsuccessful.
Sometimes expected type is just `T`, where `T` is type variable.
To support such case we take all supertypes and check them instead.
This commit is contained in:
Stanislav Erokhin
2017-05-10 12:30:18 +03:00
committed by Mikhail Zarechenskiy
parent 55dc2c11f7
commit ee16a79612
5 changed files with 60 additions and 8 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
@@ -77,7 +78,7 @@ internal object CheckArguments : ResolutionPart {
else {
// callable reference resolution will be run after choosing single descriptor
postponeCallableReferenceArguments.add(PostponeCallableReferenceArgument(argument, expectedType))
null
checkCallableExpectedType(csBuilder, argument, expectedType)
}
}
else -> error("Incorrect argument type: $argument, ${argument.javaClass.canonicalName}.")
@@ -163,6 +164,15 @@ internal object CheckArguments : ResolutionPart {
return null
}
fun checkCallableExpectedType(
csBuilder: ConstraintSystemBuilder,
argument: CallableReferenceKotlinCallArgument,
expectedType: UnwrappedType
): KotlinCallDiagnostic? {
val notCallableTypeConstructor = csBuilder.getProperSuperTypeConstructors(expectedType).firstOrNull { !ReflectionTypes.isPossibleExpectedCallableType(it) }
return notCallableTypeConstructor?.let { NotCallableExpectedType(argument, expectedType, notCallableTypeConstructor) }
}
fun processCallableReferenceArgument(
callContext: KotlinCallContext,
kotlinCall: KotlinCall,
@@ -28,9 +28,9 @@ import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.RUNTIME_ERROR
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.*
import org.jetbrains.kotlin.resolve.calls.tower.VisibilityError
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.Variance
@@ -250,25 +250,33 @@ class ExpectedLambdaParametersCountMismatch(
val lambdaArgument: LambdaKotlinCallArgument,
val expected: Int,
val actual: Int
) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) {
) : KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(lambdaArgument, this)
}
class UnexpectedReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) {
class UnexpectedReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this)
}
class MissingReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) {
class MissingReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this)
}
class NoneCallableReferenceCandidates(val argument: CallableReferenceKotlinCallArgument) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) {
class NoneCallableReferenceCandidates(val argument: CallableReferenceKotlinCallArgument) : KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
}
class CallableReferenceCandidatesAmbiguity(
val argument: CallableReferenceKotlinCallArgument,
val candidates: Collection<CallableReferenceCandidate>
) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) {
) : KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
}
class NotCallableExpectedType(
val argument: CallableReferenceKotlinCallArgument,
val expectedType: UnwrappedType,
val notCallableTypeConstructor: TypeConstructor
) : KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCallableReferenceArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.UnwrappedType
interface ConstraintSystemOperation {
@@ -32,6 +33,8 @@ interface ConstraintSystemOperation {
fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition)
fun isProperType(type: UnwrappedType): Boolean
fun getProperSuperTypeConstructors(type: UnwrappedType): List<TypeConstructor>
}
interface ConstraintSystemBuilder : ConstraintSystemOperation {
@@ -90,6 +90,16 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
override fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) =
constraintInjector.addInitialEqualityConstraint(apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) }, a, b, position)
override fun getProperSuperTypeConstructors(type: UnwrappedType): List<TypeConstructor> {
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
val variableWithConstraints = notFixedTypeVariables[type.constructor] ?: return listOf(type.constructor)
return variableWithConstraints.constraints.mapNotNull {
if (it.kind == ConstraintKind.LOWER) return@mapNotNull null
it.type.constructor.takeUnless { allTypeVariables.containsKey(it) }
}
}
// ConstraintSystemBuilder
private fun transactionRegisterVariable(variable: NewTypeVariable) {
if (state != State.TRANSACTION) return
@@ -149,5 +149,26 @@ class ReflectionTypes(module: ModuleDescriptor, private val notFoundClasses: Not
return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kPropertyClass,
listOf(StarProjectionImpl(kPropertyClass.typeConstructor.parameters.single())))
}
fun isPossibleExpectedCallableType(typeConstructor: TypeConstructor): Boolean {
val descriptor = typeConstructor.declarationDescriptor as? ClassDescriptor ?: return false
if (KotlinBuiltIns.isAny(descriptor)) return true
val shortName = descriptor.name.asString()
val packageName = DescriptorUtils.getFqName(descriptor).parent().toSafe()
if (packageName == KOTLIN_REFLECT_FQ_NAME) {
return shortName.startsWith("KFunction") // KFunctionN, KFunction
|| shortName.startsWith("KProperty") // KPropertyN, KProperty
|| shortName.startsWith("KMutableProperty") // KMutablePropertyN, KMutableProperty
|| shortName == "KCallable" || shortName == "KAnnotatedElement"
}
if (packageName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME) {
return shortName.startsWith("Function") // FunctionN, Function
}
return false
}
}
}