[NI] Report unsafe implicit invoke accordingly to OI

This reverts commit df046683cc.
KT-30695
This commit is contained in:
Pavel Kirpichenkov
2020-02-13 16:13:53 +03:00
parent 1a2d28d25b
commit b161839092
30 changed files with 286 additions and 62 deletions
@@ -35,11 +35,11 @@ fun resolveKtPrimitive(
argument: KotlinCallArgument,
expectedType: UnwrappedType?,
diagnosticsHolder: KotlinDiagnosticsHolder,
isReceiver: Boolean,
convertedType: UnwrappedType?
receiverInfo: ReceiverInfo,
convertedType: UnwrappedType?,
): ResolvedAtom = when (argument) {
is SimpleKotlinCallArgument ->
checkSimpleArgument(csBuilder, argument, expectedType, diagnosticsHolder, isReceiver, convertedType)
checkSimpleArgument(csBuilder, argument, expectedType, diagnosticsHolder, receiverInfo, convertedType)
is LambdaKotlinCallArgument ->
preprocessLambdaArgument(csBuilder, argument, expectedType, diagnosticsHolder)
@@ -132,7 +132,7 @@ class PostponedArgumentsAnalyzer(
val subResolvedKtPrimitives = returnArgumentsInfo.nonErrorArguments.map {
resolveKtPrimitive(
c.getBuilder(), it, lambda.returnType.let(::substitute), diagnosticHolder, isReceiver = false, convertedType = null
c.getBuilder(), it, lambda.returnType.let(::substitute), diagnosticHolder, ReceiverInfo.notReceiver, convertedType = null
)
}
@@ -288,7 +288,7 @@ internal object CheckExplicitReceiverKindConsistency : ResolutionPart() {
private fun KotlinResolutionCandidate.resolveKotlinArgument(
argument: KotlinCallArgument,
candidateParameter: ParameterDescriptor?,
isReceiver: Boolean
receiverInfo: ReceiverInfo
) {
val expectedType = candidateParameter?.let { prepareExpectedType(argument, candidateParameter) }
val convertedArgument = if (expectedType != null && shouldRunConversionForConstants(expectedType)) {
@@ -306,7 +306,7 @@ private fun KotlinResolutionCandidate.resolveKotlinArgument(
argument,
expectedType,
this,
isReceiver,
receiverInfo,
convertedArgument?.unknownIntegerType?.unwrap()
)
)
@@ -327,6 +327,30 @@ private fun KotlinResolutionCandidate.shouldRunConversionForConstants(expectedTy
return false
}
internal enum class ImplicitInvokeCheckStatus {
NO_INVOKE, INVOKE_ON_NOT_NULL_VARIABLE, UNSAFE_INVOKE_REPORTED
}
private fun KotlinResolutionCandidate.checkUnsafeImplicitInvokeAfterSafeCall(argument: SimpleKotlinCallArgument): ImplicitInvokeCheckStatus {
val variableForInvoke = variableCandidateIfInvoke ?: return ImplicitInvokeCheckStatus.NO_INVOKE
val receiverArgument = with(variableForInvoke.resolvedCall) {
when (explicitReceiverKind) {
DISPATCH_RECEIVER -> dispatchReceiverArgument
EXTENSION_RECEIVER,
BOTH_RECEIVERS -> extensionReceiverArgument
NO_EXPLICIT_RECEIVER -> return ImplicitInvokeCheckStatus.INVOKE_ON_NOT_NULL_VARIABLE
}
} ?: error("Receiver kind does not match receiver argument")
if (receiverArgument.receiver.stableType.isNullable()) {
addDiagnostic(UnsafeCallError(argument, isForImplicitInvoke = true))
return ImplicitInvokeCheckStatus.UNSAFE_INVOKE_REPORTED
}
return ImplicitInvokeCheckStatus.INVOKE_ON_NOT_NULL_VARIABLE
}
private fun KotlinResolutionCandidate.prepareExpectedType(
argument: KotlinCallArgument,
candidateParameter: ParameterDescriptor
@@ -388,21 +412,40 @@ private fun KotlinResolutionCandidate.getExpectedTypeWithSAMConversion(
internal object CheckReceivers : ResolutionPart() {
private fun KotlinResolutionCandidate.checkReceiver(
receiverArgument: SimpleKotlinCallArgument?,
receiverParameter: ReceiverParameterDescriptor?
receiverParameter: ReceiverParameterDescriptor?,
shouldCheckImplicitInvoke: Boolean,
) {
if ((receiverArgument == null) != (receiverParameter == null)) {
error("Inconsistency receiver state for call $kotlinCall and candidate descriptor: $candidateDescriptor")
}
if (receiverArgument == null || receiverParameter == null) return
resolveKotlinArgument(receiverArgument, receiverParameter, isReceiver = true)
val implicitInvokeState = if (shouldCheckImplicitInvoke) {
checkUnsafeImplicitInvokeAfterSafeCall(receiverArgument)
} else ImplicitInvokeCheckStatus.NO_INVOKE
val receiverInfo = ReceiverInfo(
isReceiver = true,
shouldReportUnsafeCall = implicitInvokeState != ImplicitInvokeCheckStatus.UNSAFE_INVOKE_REPORTED,
reportUnsafeCallAsUnsafeImplicitInvoke = implicitInvokeState == ImplicitInvokeCheckStatus.INVOKE_ON_NOT_NULL_VARIABLE
)
resolveKotlinArgument(receiverArgument, receiverParameter, receiverInfo)
}
override fun KotlinResolutionCandidate.process(workIndex: Int) {
if (workIndex == 0) {
checkReceiver(resolvedCall.dispatchReceiverArgument, candidateDescriptor.dispatchReceiverParameter)
checkReceiver(
resolvedCall.dispatchReceiverArgument,
candidateDescriptor.dispatchReceiverParameter,
shouldCheckImplicitInvoke = true,
)
} else {
checkReceiver(resolvedCall.extensionReceiverArgument, candidateDescriptor.extensionReceiverParameter)
checkReceiver(
resolvedCall.extensionReceiverArgument,
candidateDescriptor.extensionReceiverParameter,
shouldCheckImplicitInvoke = false, // reproduce old inference behaviour
)
}
}
@@ -412,7 +455,7 @@ internal object CheckReceivers : ResolutionPart() {
internal object CheckArgumentsInParenthesis : ResolutionPart() {
override fun KotlinResolutionCandidate.process(workIndex: Int) {
val argument = kotlinCall.argumentsInParenthesis[workIndex]
resolveKotlinArgument(argument, resolvedCall.argumentToCandidateParameter[argument], isReceiver = false)
resolveKotlinArgument(argument, resolvedCall.argumentToCandidateParameter[argument], ReceiverInfo.notReceiver)
}
override fun KotlinResolutionCandidate.workCount() = kotlinCall.argumentsInParenthesis.size
@@ -422,7 +465,7 @@ internal object CheckExternalArgument : ResolutionPart() {
override fun KotlinResolutionCandidate.process(workIndex: Int) {
val argument = kotlinCall.externalArgument ?: return
resolveKotlinArgument(argument, resolvedCall.argumentToCandidateParameter[argument], isReceiver = false)
resolveKotlinArgument(argument, resolvedCall.argumentToCandidateParameter[argument], ReceiverInfo.notReceiver)
}
}
@@ -485,14 +528,14 @@ internal object ErrorDescriptorResolutionPart : ResolutionPart() {
resolvedCall.argumentToCandidateParameter = emptyMap()
kotlinCall.explicitReceiver?.safeAs<SimpleKotlinCallArgument>()?.let {
resolveKotlinArgument(it, null, isReceiver = true)
resolveKotlinArgument(it, null, ReceiverInfo.notReceiver)
}
for (argument in kotlinCall.argumentsInParenthesis) {
resolveKotlinArgument(argument, null, isReceiver = true)
resolveKotlinArgument(argument, null, ReceiverInfo.notReceiver)
}
kotlinCall.externalArgument?.let {
resolveKotlinArgument(it, null, isReceiver = true)
resolveKotlinArgument(it, null, ReceiverInfo.notReceiver)
}
}
}
@@ -31,17 +31,30 @@ import org.jetbrains.kotlin.types.checker.hasSupertypeWithGivenTypeConstructor
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.typeUtil.supertypes
class ReceiverInfo(
val isReceiver: Boolean,
val shouldReportUnsafeCall: Boolean, // should not report if unsafe implicit invoke has been reported already
val reportUnsafeCallAsUnsafeImplicitInvoke: Boolean,
) {
init {
assert(!reportUnsafeCallAsUnsafeImplicitInvoke || shouldReportUnsafeCall) { "Inconsistent receiver info" }
}
companion object {
val notReceiver = ReceiverInfo(isReceiver = false, shouldReportUnsafeCall = true, reportUnsafeCallAsUnsafeImplicitInvoke = false)
}
}
fun checkSimpleArgument(
csBuilder: ConstraintSystemBuilder,
argument: SimpleKotlinCallArgument,
expectedType: UnwrappedType?,
diagnosticsHolder: KotlinDiagnosticsHolder,
isReceiver: Boolean,
receiverInfo: ReceiverInfo,
convertedType: UnwrappedType?
): ResolvedAtom = when (argument) {
is ExpressionKotlinCallArgument -> checkExpressionArgument(csBuilder, argument, expectedType, diagnosticsHolder, isReceiver, convertedType)
is SubKotlinCallArgument -> checkSubCallArgument(csBuilder, argument, expectedType, diagnosticsHolder, isReceiver)
is ExpressionKotlinCallArgument -> checkExpressionArgument(csBuilder, argument, expectedType, diagnosticsHolder, receiverInfo.isReceiver, convertedType)
is SubKotlinCallArgument -> checkSubCallArgument(csBuilder, argument, expectedType, diagnosticsHolder, receiverInfo)
else -> unexpectedArgument(argument)
}
@@ -164,14 +177,14 @@ private fun checkSubCallArgument(
subCallArgument: SubKotlinCallArgument,
expectedType: UnwrappedType?,
diagnosticsHolder: KotlinDiagnosticsHolder,
isReceiver: Boolean
receiverInfo: ReceiverInfo,
): ResolvedAtom {
val subCallResult = subCallArgument.callResult
if (expectedType == null) return subCallResult
val expectedNullableType = expectedType.makeNullableAsSpecified(true)
val position = if (isReceiver) ReceiverConstraintPosition(subCallArgument) else ArgumentConstraintPosition(subCallArgument)
val position = if (receiverInfo.isReceiver) ReceiverConstraintPosition(subCallArgument) else ArgumentConstraintPosition(subCallArgument)
// subArgument cannot has stable smartcast
// return type can contains fixed type variables
@@ -183,10 +196,15 @@ private fun checkSubCallArgument(
return subCallResult
}
if (isReceiver && !csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedType, position) &&
csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedNullableType, position)
if (receiverInfo.isReceiver
&& !csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedType, position)
&& csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedNullableType, position)
) {
diagnosticsHolder.addDiagnostic(UnsafeCallError(subCallArgument))
if (receiverInfo.shouldReportUnsafeCall) {
diagnosticsHolder.addDiagnostic(
UnsafeCallError(subCallArgument, isForImplicitInvoke = receiverInfo.reportUnsafeCallAsUnsafeImplicitInvoke)
)
}
return subCallResult
}
@@ -169,7 +169,10 @@ class UnstableSmartCastDiagnosticError(
targetType: UnwrappedType,
) : UnstableSmartCast(argument, targetType, RESOLVED_WITH_ERROR)
class UnsafeCallError(val receiver: SimpleKotlinCallArgument) : KotlinCallDiagnostic(MAY_THROW_RUNTIME_ERROR) {
class UnsafeCallError(
val receiver: SimpleKotlinCallArgument,
val isForImplicitInvoke: Boolean = false
) : KotlinCallDiagnostic(MAY_THROW_RUNTIME_ERROR) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallReceiver(receiver, this)
}