[NI] Report unsafe implicit invoke accordingly to OI
This reverts commit df046683cc.
KT-30695
This commit is contained in:
+3
-3
@@ -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)
|
||||
|
||||
+1
-1
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+54
-11
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
-8
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user