K1: report TYPE_MISMATCH for incorrect assignments in builder
#KT-54004 Fixed
This commit is contained in:
committed by
Space Team
parent
0940707fd4
commit
0080f16cc2
@@ -69,7 +69,8 @@ private val DEFAULT_CALL_CHECKERS = listOf(
|
|||||||
UnitConversionCallChecker, FunInterfaceConstructorReferenceChecker, NullableExtensionOperatorWithSafeCallChecker,
|
UnitConversionCallChecker, FunInterfaceConstructorReferenceChecker, NullableExtensionOperatorWithSafeCallChecker,
|
||||||
ReferencingToUnderscoreNamedParameterOfCatchBlockChecker, VarargWrongExecutionOrderChecker, SelfCallInNestedObjectConstructorChecker,
|
ReferencingToUnderscoreNamedParameterOfCatchBlockChecker, VarargWrongExecutionOrderChecker, SelfCallInNestedObjectConstructorChecker,
|
||||||
NewSchemeOfIntegerOperatorResolutionChecker, EnumEntryVsCompanionPriorityCallChecker, CompanionInParenthesesLHSCallChecker,
|
NewSchemeOfIntegerOperatorResolutionChecker, EnumEntryVsCompanionPriorityCallChecker, CompanionInParenthesesLHSCallChecker,
|
||||||
ResolutionToPrivateConstructorOfSealedClassChecker, EqualityCallChecker, UnsupportedUntilOperatorChecker
|
ResolutionToPrivateConstructorOfSealedClassChecker, EqualityCallChecker, UnsupportedUntilOperatorChecker,
|
||||||
|
BuilderInferenceAssignmentChecker,
|
||||||
)
|
)
|
||||||
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
||||||
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(
|
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.IdentifierInfo
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.util.getType
|
||||||
|
import org.jetbrains.kotlin.types.StubTypeForBuilderInference
|
||||||
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
|
import org.jetbrains.kotlin.types.expressions.BasicExpressionTypingVisitor
|
||||||
|
import org.jetbrains.kotlin.types.isError
|
||||||
|
|
||||||
|
object BuilderInferenceAssignmentChecker : CallChecker {
|
||||||
|
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||||
|
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||||
|
if (resultingDescriptor !is PropertyDescriptor) return
|
||||||
|
if (resolvedCall.candidateDescriptor.returnType !is StubTypeForBuilderInference) return
|
||||||
|
if (reportOn !is KtNameReferenceExpression) return
|
||||||
|
val binaryExpression = reportOn.getParentOfType<KtBinaryExpression>(strict = true) ?: return
|
||||||
|
if (!BasicExpressionTypingVisitor.isLValue(reportOn, binaryExpression)) return
|
||||||
|
|
||||||
|
val leftType = resultingDescriptor.returnType?.takeIf { !it.isError } ?: return
|
||||||
|
val right = binaryExpression.right ?: return
|
||||||
|
val rightType = right.getType(context.trace.bindingContext) ?: return
|
||||||
|
|
||||||
|
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(rightType, leftType)) {
|
||||||
|
val dfi = context.dataFlowInfo
|
||||||
|
val dfvFactory = context.dataFlowValueFactory
|
||||||
|
val stableTypesFromDataFlow = dfi.getStableTypes(
|
||||||
|
dfvFactory.createDataFlowValue(right, rightType, context.trace.bindingContext, context.moduleDescriptor),
|
||||||
|
context.languageVersionSettings
|
||||||
|
)
|
||||||
|
if (stableTypesFromDataFlow.none {
|
||||||
|
KotlinTypeChecker.DEFAULT.isSubtypeOf(it, leftType)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
context.trace.report(Errors.TYPE_MISMATCH.on(right, leftType, rightType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-5
@@ -5,16 +5,15 @@ class Foo<T : Any> {
|
|||||||
|
|
||||||
fun <T : Any> myBuilder(block: Foo<T>.() -> Unit) : Foo<T> = Foo<T>().apply(block)
|
fun <T : Any> myBuilder(block: Foo<T>.() -> Unit) : Foo<T> = Foo<T>().apply(block)
|
||||||
|
|
||||||
fun main() {
|
fun main(arg: Any) {
|
||||||
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>myBuilder<!> {
|
|
||||||
a = <!ASSIGNMENT_TYPE_MISMATCH!>"some string"<!>
|
|
||||||
}
|
|
||||||
|
|
||||||
val x = 57
|
val x = 57
|
||||||
val value = myBuilder {
|
val value = myBuilder {
|
||||||
doSmthng("one ")
|
doSmthng("one ")
|
||||||
a = <!ASSIGNMENT_TYPE_MISMATCH!>57<!>
|
a = <!ASSIGNMENT_TYPE_MISMATCH!>57<!>
|
||||||
a = <!ASSIGNMENT_TYPE_MISMATCH!>x<!>
|
a = <!ASSIGNMENT_TYPE_MISMATCH!>x<!>
|
||||||
|
if (arg is String) {
|
||||||
|
a = arg
|
||||||
|
}
|
||||||
}
|
}
|
||||||
println(value.a?.count { it in 'l' .. 'q' })
|
println(value.a?.count { it in 'l' .. 'q' })
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -5,16 +5,15 @@ class Foo<T : Any> {
|
|||||||
|
|
||||||
fun <T : Any> myBuilder(block: Foo<T>.() -> Unit) : Foo<T> = Foo<T>().apply(block)
|
fun <T : Any> myBuilder(block: Foo<T>.() -> Unit) : Foo<T> = Foo<T>().apply(block)
|
||||||
|
|
||||||
fun main() {
|
fun main(arg: Any) {
|
||||||
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>myBuilder<!> {
|
|
||||||
<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>a<!> = "some string"
|
|
||||||
}
|
|
||||||
|
|
||||||
val x = 57
|
val x = 57
|
||||||
val value = myBuilder {
|
val value = myBuilder {
|
||||||
doSmthng("one ")
|
doSmthng("one ")
|
||||||
a = 57
|
a = <!TYPE_MISMATCH!>57<!>
|
||||||
a = x
|
a = <!TYPE_MISMATCH!>x<!>
|
||||||
|
if (arg is String) {
|
||||||
|
a = arg
|
||||||
|
}
|
||||||
}
|
}
|
||||||
println(value.a?.count { it in 'l' .. 'q' })
|
println(value.a?.count { it in 'l' .. 'q' })
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
package
|
package
|
||||||
|
|
||||||
public fun main(): kotlin.Unit
|
public fun main(/*0*/ arg: kotlin.Any): kotlin.Unit
|
||||||
public fun </*0*/ T : kotlin.Any> myBuilder(/*0*/ block: Foo<T>.() -> kotlin.Unit): Foo<T>
|
public fun </*0*/ T : kotlin.Any> myBuilder(/*0*/ block: Foo<T>.() -> kotlin.Unit): Foo<T>
|
||||||
|
|
||||||
public final class Foo</*0*/ T : kotlin.Any> {
|
public final class Foo</*0*/ T : kotlin.Any> {
|
||||||
@@ -11,3 +11,4 @@ public final class Foo</*0*/ T : kotlin.Any> {
|
|||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user