Add deprecation warning for false-negative TYPE_MISMATCH
See KT-49404 for details In K1, within SubstitutingScope we approximate almost all the types containing captured types are being approximated to either a lower or an upper bound. While mostly, it's being done correctly there are some problems with approximations for flexible types So, the parameter's type of A<in Any>::foo is approximated to Inv<in Any!>, thus allowing to use Inv<*>, while it's obviously unsound. NB: For the similar example, in B, there are regular TYPE_MISMATCH because parameter for B<in Any>::foo is approximated to Nothing Also, it's important to say that - in K2 everything works because we don't use type approximation there - approximation algorithm that works incorrectly is only being used in K1 ^KT-54332 Fixed
This commit is contained in:
committed by
Space Team
parent
4b455c0e51
commit
31ba1f1534
@@ -894,6 +894,9 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory0<PsiElement> RESOLUTION_TO_PRIVATE_CONSTRUCTOR_OF_SEALED_CLASS = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> TYPE_MISMATCH_WARNING_FOR_INCORRECT_CAPTURE_APPROXIMATION = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> RECEIVER_TYPE_MISMATCH_WARNING_FOR_INCORRECT_CAPTURE_APPROXIMATION = DiagnosticFactory2.create(WARNING);
|
||||
|
||||
// Type inference
|
||||
|
||||
DiagnosticFactory0<KtParameter> CANNOT_INFER_PARAMETER_TYPE = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
+10
@@ -462,6 +462,16 @@ public class DefaultErrorMessages {
|
||||
|
||||
|
||||
MAP.put(RESOLUTION_TO_PRIVATE_CONSTRUCTOR_OF_SEALED_CLASS, "The private constructor of a sealed class will become inaccessible here in future. See https://youtrack.jetbrains.com/issue/KT-44866 for details");
|
||||
MAP.put(
|
||||
TYPE_MISMATCH_WARNING_FOR_INCORRECT_CAPTURE_APPROXIMATION,
|
||||
"Type mismatch: inferred type is {1} but {0} was expected. This warning will be an error soon. See https://youtrack.jetbrains.com/issue/KT-49404 for details",
|
||||
RENDER_TYPE, RENDER_TYPE
|
||||
);
|
||||
MAP.put(
|
||||
RECEIVER_TYPE_MISMATCH_WARNING_FOR_INCORRECT_CAPTURE_APPROXIMATION,
|
||||
"Extension receiver type mismatch: inferred type is {1} but {0} was expected. This warning will be an error soon. See https://youtrack.jetbrains.com/issue/KT-49404 for details",
|
||||
RENDER_TYPE, RENDER_TYPE
|
||||
);
|
||||
MAP.put(LOCAL_EXTENSION_PROPERTY, "Local extension properties are not allowed");
|
||||
MAP.put(LOCAL_VARIABLE_WITH_GETTER, "Local variables are not allowed to have getters");
|
||||
MAP.put(LOCAL_VARIABLE_WITH_SETTER, "Local variables are not allowed to have setters");
|
||||
|
||||
@@ -70,7 +70,7 @@ private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
ReferencingToUnderscoreNamedParameterOfCatchBlockChecker, VarargWrongExecutionOrderChecker, SelfCallInNestedObjectConstructorChecker,
|
||||
NewSchemeOfIntegerOperatorResolutionChecker, EnumEntryVsCompanionPriorityCallChecker, CompanionInParenthesesLHSCallChecker,
|
||||
ResolutionToPrivateConstructorOfSealedClassChecker, EqualityCallChecker, UnsupportedUntilOperatorChecker,
|
||||
BuilderInferenceAssignmentChecker,
|
||||
BuilderInferenceAssignmentChecker, IncorrectCapturedApproximationCallChecker,
|
||||
)
|
||||
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
||||
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.isFunctionOrSuspendFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.isCaptured
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.wrapWithCapturingSubstitution
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getEffectiveExpectedType
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getType
|
||||
import org.jetbrains.kotlin.resolve.sam.getFunctionTypeForSamType
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.contains
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
|
||||
object IncorrectCapturedApproximationCallChecker : CallChecker {
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
if (!resolvedCall.isReallySuccess()) return
|
||||
val dispatchReceiverType = resolvedCall.smartCastDispatchReceiverType ?: resolvedCall.dispatchReceiver?.type ?: return
|
||||
if (dispatchReceiverType.arguments.all { it.projectionKind == Variance.INVARIANT && !it.type.isCaptured() }) return
|
||||
|
||||
val substitutor =
|
||||
TypeSubstitutor.create(dispatchReceiverType)
|
||||
.substitution.wrapWithCapturingSubstitution(needApproximation = false).buildSubstitutor()
|
||||
|
||||
val functionDescriptor = resolvedCall.resultingDescriptor.original as? FunctionDescriptor ?: return
|
||||
|
||||
val capturedSubstituted = functionDescriptor.substitute(substitutor) ?: return
|
||||
|
||||
val indexedArguments = resolvedCall.valueArgumentsByIndex ?: return
|
||||
for ((index, parameter) in capturedSubstituted.valueParameters.withIndex()) {
|
||||
for (argument in indexedArguments[index].arguments) {
|
||||
val expectedType =
|
||||
getEffectiveExpectedType(parameter, argument, context.resolutionContext)
|
||||
|
||||
val argumentExpression = argument.getArgumentExpression() ?: continue
|
||||
val expressionType = argumentExpression.getType(context.trace.bindingContext) ?: continue
|
||||
|
||||
val dataFlowValue =
|
||||
context.dataFlowValueFactory.createDataFlowValue(argumentExpression, expressionType, context.resolutionContext)
|
||||
if (shouldWarningBeReported(expressionType, expectedType, dataFlowValue, context)) {
|
||||
context.trace.report(
|
||||
Errors.TYPE_MISMATCH_WARNING_FOR_INCORRECT_CAPTURE_APPROXIMATION.on(
|
||||
argumentExpression, expectedType, expressionType
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
capturedSubstituted.extensionReceiverParameter?.let { extensionReceiverParameter ->
|
||||
val extensionReceiver = resolvedCall.extensionReceiver ?: return@let
|
||||
|
||||
val dataFlowValue =
|
||||
context.dataFlowValueFactory.createDataFlowValue(extensionReceiver, context.resolutionContext)
|
||||
|
||||
if (shouldWarningBeReported(extensionReceiver.type, extensionReceiverParameter.type, dataFlowValue, context)) {
|
||||
val expression = (extensionReceiver as? ExpressionReceiver)?.expression ?: reportOn
|
||||
context.trace.report(
|
||||
Errors.RECEIVER_TYPE_MISMATCH_WARNING_FOR_INCORRECT_CAPTURE_APPROXIMATION.on(
|
||||
expression, extensionReceiverParameter.type, extensionReceiver.type
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldWarningBeReported(
|
||||
expressionType: KotlinType,
|
||||
expectedType: KotlinType,
|
||||
dataFlowValue: DataFlowValue,
|
||||
context: CallCheckerContext,
|
||||
): Boolean {
|
||||
if (expectedType.arguments.none { arg -> !arg.isStarProjection && arg.type.contains(UnwrappedType::isCaptured) }) return false
|
||||
if (expressionType.isSubtypeOf(expectedType)) return false
|
||||
|
||||
val samExpectedType = getFunctionTypeForSamType(
|
||||
expectedType, context.callComponents.samConversionResolver, context.callComponents.samConversionOracle,
|
||||
)
|
||||
|
||||
if (expectedType.isFunctionOrSuspendFunctionType || samExpectedType?.isFunctionOrSuspendFunctionType == true) return false
|
||||
|
||||
return context.dataFlowInfo.getCollectedTypes(
|
||||
dataFlowValue,
|
||||
context.languageVersionSettings,
|
||||
).none { it.isSubtypeOf(expectedType) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user