Skip to content

Commit

Permalink
K1: add more tests for BI assignment checker, fix corner cases
Browse files Browse the repository at this point in the history
Related to KT-54004

(cherry picked from commit 50cd560)
  • Loading branch information
mglukhikh authored and qodana-bot committed Oct 27, 2022
1 parent bc0aecc commit 34ae02b
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,11 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
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
Expand All @@ -34,6 +29,7 @@ object BuilderInferenceAssignmentChecker : CallChecker {
val callElement = resolvedCall.call.callElement
if (callElement !is KtNameReferenceExpression) return
val binaryExpression = callElement.getParentOfType<KtBinaryExpression>(strict = true) ?: return
if (binaryExpression.operationToken != KtTokens.EQ) return
if (!BasicExpressionTypingVisitor.isLValue(callElement, binaryExpression)) return

val leftType = resultingDescriptor.returnType?.takeIf { !it.isError } ?: return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ fun main(arg: Any) {
val x = 57
val value = myBuilder {
doSmthng("one ")
a = <!ASSIGNMENT_TYPE_MISMATCH!>57<!>
run { a; this }.a = <!ASSIGNMENT_TYPE_MISMATCH!>10<!>
a += 1
this.a = <!ASSIGNMENT_TYPE_MISMATCH!>57<!>
this.(a) = 57
a = <!ASSIGNMENT_TYPE_MISMATCH!>x<!>
(a) = <!ASSIGNMENT_TYPE_MISMATCH!>x<!>
a.<!FUNCTION_CALL_EXPECTED!>hashCode<!> = 99
if (arg is String) {
a = arg
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ fun main(arg: Any) {
val x = 57
val value = myBuilder {
doSmthng("one ")
a = <!TYPE_MISMATCH!>57<!>
run { a; this }.a = <!TYPE_MISMATCH, TYPE_MISMATCH!>10<!>
<!BUILDER_INFERENCE_STUB_RECEIVER, STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY, TYPE_MISMATCH, TYPE_MISMATCH!>a<!> <!OVERLOAD_RESOLUTION_AMBIGUITY_BECAUSE_OF_STUB_TYPES!>+=<!> 1
this.a = <!TYPE_MISMATCH!>57<!>
this.<!ILLEGAL_SELECTOR, VARIABLE_EXPECTED!>(a)<!> = <!TYPE_MISMATCH!>57<!>
a = <!TYPE_MISMATCH!>x<!>
(a) = <!TYPE_MISMATCH!>x<!>
<!TYPE_MISMATCH!>a<!>.<!FUNCTION_CALL_EXPECTED, VARIABLE_EXPECTED!>hashCode<!> = <!TYPE_MISMATCH, TYPE_MISMATCH!>99<!>
if (arg is String) {
a = arg
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// WITH_REFLECT
// FIR_DUMP
import kotlin.reflect.*

interface Foo<T : Any> {
Expand Down Expand Up @@ -57,4 +58,20 @@ fun main(arg: Any, condition: Boolean) {
a = <!ASSIGNMENT_TYPE_MISMATCH!>67<!>
}
}

// See KT-54664
val value3 = myBuilder {
accept("")
a = 45
bar(::a)
}

fun baz(t: Int) {}

val value4 = myBuilder {
accept("")
a = 45
b[0] = 123
baz(a)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
FILE: unsafeAssignmentExtra.fir.kt
public abstract interface Foo<T : R|kotlin/Any|> : R|kotlin/Any| {
public abstract var a: R|T|
public get(): R|T|
public set(value: R|T|): R|kotlin/Unit|

public abstract val b: R|kotlin/Array<T>|
public get(): R|kotlin/Array<T>|

public abstract fun accept(arg: R|T|): R|kotlin/Unit|

}
public final class FooImpl<T : R|kotlin/Any|> : R|Foo<T>| {
public constructor<T : R|kotlin/Any|>(): R|FooImpl<T>| {
super<R|kotlin/Any|>()
}

}
public final fun bar(p: R|kotlin/reflect/KMutableProperty0<kotlin/Int>|): R|kotlin/Unit| {
R|<local>/p|.R|SubstitutionOverride<kotlin/reflect/KMutableProperty0.set: R|kotlin/Unit|>|(Int(100))
}
public final fun <T : R|kotlin/Any|> myBuilder(block: R|Foo<T>.() -> kotlin/Unit|): R|Foo<T>| {
^myBuilder R|/FooImpl.FooImpl|<R|T|>().R|kotlin/apply|<R|FooImpl<T>|>(R|<local>/block|)
}
public final fun <T : R|kotlin/Any|> R|Foo<T>|.change(block: R|Foo<T>.() -> kotlin/Unit|): R|Foo<T>| {
R|<local>/block|.R|SubstitutionOverride<kotlin/Function1.invoke: R|kotlin/Unit|>|(this@R|/change|)
^change this@R|/change|
}
public final fun main(arg: R|kotlin/Any|, condition: R|kotlin/Boolean|): R|kotlin/Unit| {
lval value: R|Foo<kotlin/Int>| = R|/myBuilder|<R|kotlin/Int|>(<L> = myBuilder@fun R|Foo<kotlin/Int>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.b: R|kotlin/Array<Stub (chain inference): TypeVariable(T)>|>|.R|SubstitutionOverride<kotlin/Array.set: R|kotlin/Unit|>|(Int(0), Int(123))
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(45)
lval <unary>: R|kotlin/Int| = this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>|
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = R|<local>/<unary>|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
R|<local>/<unary>|
R|/bar|(::R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>|)
when () {
(this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| is R|kotlin/Int|) -> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(67)
lval <unary>: R|kotlin/Int| = this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>|
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = R|<local>/<unary>|.<Ambiguity: dec, [kotlin/dec, kotlin/dec]>#()
R|<local>/<unary>|
R|/bar|(::R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>|)
}
}

when (R|<local>/condition|) {
==($subj$, Boolean(true)) -> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(87)
}
==($subj$, Boolean(false)) -> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(65)
}
}

lval x: <ERROR TYPE REF: Ambiguity: getValue, [kotlin/getValue, kotlin/getValue, kotlin/getValue, kotlin/collections/getValue, kotlin/collections/getValue]>by this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>|
this@R|special/anonymous|.R|/change|<R|kotlin/Int|>(<L> = change@fun R|Foo<kotlin/Int>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(99)
}
)
}
)
lval value2: R|Foo<kotlin/String>| = R|/myBuilder|<R|kotlin/String|>(<L> = myBuilder@fun R|Foo<kotlin/String>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.accept: R|kotlin/Unit|>|(String())
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(45)
when (R|<local>/condition|) {
==($subj$, Boolean(true)) -> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(87)
}
==($subj$, Boolean(false)) -> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(65)
}
}

this@R|special/anonymous|.R|/change|<R|kotlin/String|>(<L> = change@fun R|Foo<kotlin/String>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(99)
}
)
when () {
(this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| is R|kotlin/Int|) -> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(67)
}
}

}
)
lval value3: R|Foo<kotlin/Int>| = R|/myBuilder|<R|kotlin/Int|>(<L> = myBuilder@fun R|Foo<kotlin/Int>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.accept: R|kotlin/Unit|>|(String())
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(45)
R|/bar|(::R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>|)
}
)
local final fun baz(t: R|kotlin/Int|): R|kotlin/Unit| {
}

lval value4: R|Foo<it(kotlin/Comparable<*> & java/io/Serializable)>| = R|/myBuilder|<R|it(kotlin/Comparable<*> & java/io/Serializable)|>(<L> = myBuilder@fun R|Foo<it(kotlin/Comparable<*> & java/io/Serializable)>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
this@R|special/anonymous|.R|SubstitutionOverride</Foo.accept: R|kotlin/Unit|>|(String())
this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>| = Int(45)
this@R|special/anonymous|.R|SubstitutionOverride</Foo.b: R|kotlin/Array<Stub (chain inference): TypeVariable(T)>|>|.R|SubstitutionOverride<kotlin/Array.set: R|kotlin/Unit|>|(Int(0), Int(123))
R|<local>/baz|(this@R|special/anonymous|.R|SubstitutionOverride</Foo.a: R|Stub (chain inference): TypeVariable(T)|>|)
}
)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// WITH_REFLECT
// FIR_DUMP
import kotlin.reflect.*

interface Foo<T : Any> {
Expand Down Expand Up @@ -57,4 +58,20 @@ fun main(arg: Any, condition: Boolean) {
a = <!TYPE_MISMATCH!>67<!>
}
}

// See KT-54664
val value3 = myBuilder {
accept(<!TYPE_MISMATCH!>""<!>)
a = 45
bar(<!TYPE_MISMATCH!>::a<!>)
}

fun baz(t: Int) {}

val value4 = myBuilder {
accept("")
a = 45
b[0] = 123
baz(<!TYPE_MISMATCH, TYPE_MISMATCH!>a<!>)
}
}

0 comments on commit 34ae02b

Please sign in to comment.