FIR: Choose a resolved candidate for augmented assignment when both
assign and operator candidates are unsuccessful.
This commit is contained in:
committed by
teamcityserver
parent
32bb64a225
commit
d1531f9cdd
+20
-10
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.ConstantValueKind
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) : FirPartialBodyResolveTransformer(transformer) {
|
||||
private inline val builtinTypes: BuiltinTypes get() = session.builtinTypes
|
||||
@@ -386,7 +386,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
||||
assignOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
|
||||
}
|
||||
val assignCallReference = resolvedAssignCall.calleeReference as? FirNamedReferenceWithCandidate
|
||||
val assignIsResolvable = assignCallReference?.isError == false
|
||||
val assignIsSuccessful = assignCallReference?.isError == false
|
||||
|
||||
// x = x + y
|
||||
val simpleOperatorName = FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(assignmentOperatorStatement.operation)
|
||||
@@ -395,7 +395,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
||||
simpleOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
|
||||
}
|
||||
val operatorCallReference = resolvedOperatorCall.calleeReference as? FirNamedReferenceWithCandidate
|
||||
val operatorIsResolvable = operatorCallReference?.isError == false
|
||||
val operatorIsSuccessful = operatorCallReference?.isError == false
|
||||
|
||||
fun operatorReturnTypeMatches(candidate: Candidate): Boolean {
|
||||
// After KT-45503, non-assign flavor of operator is checked more strictly: the return type must be assignable to the variable.
|
||||
@@ -408,8 +408,8 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
||||
leftArgument.typeRef.coneType
|
||||
)
|
||||
}
|
||||
// following `!!` is safe since `operatorIsResolvable = true` implies `operatorCallReference != null`
|
||||
val operatorReturnTypeMatches = operatorIsResolvable && operatorReturnTypeMatches(operatorCallReference!!.candidate)
|
||||
// following `!!` is safe since `operatorIsSuccessful = true` implies `operatorCallReference != null`
|
||||
val operatorReturnTypeMatches = operatorIsSuccessful && operatorReturnTypeMatches(operatorCallReference!!.candidate)
|
||||
|
||||
val lhsReference = leftArgument.toResolvedCallableReference()
|
||||
val lhsSymbol = lhsReference?.resolvedSymbol as? FirVariableSymbol<*>
|
||||
@@ -467,11 +467,21 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
||||
}
|
||||
|
||||
return when {
|
||||
assignIsResolvable && !lhsIsVar -> chooseAssign()
|
||||
!assignIsResolvable && !operatorIsResolvable -> chooseAssign()
|
||||
!assignIsResolvable && operatorIsResolvable -> chooseOperator()
|
||||
assignIsResolvable && !operatorIsResolvable -> chooseAssign()
|
||||
assignIsResolvable && operatorIsResolvable && !operatorReturnTypeMatches -> chooseAssign()
|
||||
assignIsSuccessful && !lhsIsVar -> chooseAssign()
|
||||
!assignIsSuccessful && !operatorIsSuccessful -> {
|
||||
// If neither candidate is successful, choose whichever is resolved, prioritizing assign
|
||||
val isAssignResolved = assignCallReference.safeAs<FirErrorReferenceWithCandidate>()?.diagnostic !is ConeUnresolvedNameError
|
||||
val isOperatorResolved =
|
||||
operatorCallReference.safeAs<FirErrorReferenceWithCandidate>()?.diagnostic !is ConeUnresolvedNameError
|
||||
when {
|
||||
isAssignResolved -> chooseAssign()
|
||||
isOperatorResolved -> chooseOperator()
|
||||
else -> chooseAssign()
|
||||
}
|
||||
}
|
||||
!assignIsSuccessful && operatorIsSuccessful -> chooseOperator()
|
||||
assignIsSuccessful && !operatorIsSuccessful -> chooseAssign()
|
||||
assignIsSuccessful && operatorIsSuccessful && !operatorReturnTypeMatches -> chooseAssign()
|
||||
else -> reportAmbiguity()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ fun f(): Unit {
|
||||
x <!UNSAFE_OPERATOR_CALL!>+<!> 1
|
||||
x <!UNSAFE_INFIX_CALL!>plus<!> 1
|
||||
x <!UNSAFE_OPERATOR_CALL!><<!> 1
|
||||
x <!UNRESOLVED_REFERENCE!>+=<!> 1
|
||||
x <!UNSAFE_OPERATOR_CALL!>+=<!> 1
|
||||
|
||||
x == 1
|
||||
x != 1
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
class A {
|
||||
operator fun plusAssign(s: String) {}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
var a: A? = A()
|
||||
a <!UNSAFE_OPERATOR_CALL!>+=<!> ""
|
||||
}
|
||||
|
||||
class B {
|
||||
operator fun plus(other: B) = this
|
||||
}
|
||||
|
||||
fun test2() {
|
||||
var b: B? = B()
|
||||
b <!UNRESOLVED_REFERENCE!>+=<!> B()
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
class A {
|
||||
operator fun plusAssign(s: String) {}
|
||||
}
|
||||
|
||||
Vendored
+8
-8
@@ -1,14 +1,14 @@
|
||||
fun intBinEq() {
|
||||
var x = 0
|
||||
x <!UNRESOLVED_REFERENCE!>+=<!> 'a'
|
||||
x <!NONE_APPLICABLE!>+=<!> 'a'
|
||||
x += 1.toByte()
|
||||
x += 1.toShort()
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1L<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1f<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1.0<!>
|
||||
x <!UNRESOLVED_REFERENCE!>*=<!> 'a'
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.toByte()<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.toShort()<!>
|
||||
x <!NONE_APPLICABLE!>*=<!> 'a'
|
||||
x *= 1.toByte()
|
||||
x *= 1.toShort()
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1L<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1f<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.0<!>
|
||||
@@ -16,16 +16,16 @@ fun intBinEq() {
|
||||
|
||||
fun shortBinEq() {
|
||||
var x = 0.toShort()
|
||||
x <!UNRESOLVED_REFERENCE!>+=<!> 'a'
|
||||
x <!NONE_APPLICABLE!>+=<!> 'a'
|
||||
x += 1.toByte()
|
||||
x += 1.toShort()
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1L<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1f<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1.0<!>
|
||||
|
||||
x <!UNRESOLVED_REFERENCE!>*=<!> 'a'
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.toByte()<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.toShort()<!>
|
||||
x <!NONE_APPLICABLE!>*=<!> 'a'
|
||||
x *= 1.toByte()
|
||||
x *= 1.toShort()
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1L<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1f<!>
|
||||
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.0<!>
|
||||
|
||||
@@ -7,7 +7,7 @@ fun foo(): String {
|
||||
var t: String? = "y"
|
||||
if (t == null) t = "x"
|
||||
var x: Int? = null
|
||||
if (x == null) x <!UNRESOLVED_REFERENCE!>+=<!> null
|
||||
if (x == null) x <!UNSAFE_OPERATOR_CALL!>+=<!> null
|
||||
return t + s
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
fun case_1() {
|
||||
var x: Int? = 11
|
||||
x!!
|
||||
try {x = null;} finally { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!> <!UNRESOLVED_REFERENCE!>+=<!> 10; }
|
||||
try {x = null;} finally { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!> <!UNSAFE_OPERATOR_CALL!>+=<!> 10; }
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 2
|
||||
|
||||
+1
-3
@@ -6,6 +6,4 @@ class A {
|
||||
fun foo(b: A) {
|
||||
var a: A? = A()
|
||||
a <caret>+= b
|
||||
}
|
||||
|
||||
/* IGNORE_FIR */
|
||||
}
|
||||
+1
-3
@@ -6,6 +6,4 @@ class A {
|
||||
fun foo(b: A) {
|
||||
var a: A? = A()
|
||||
a = a?.plus(b)
|
||||
}
|
||||
|
||||
/* IGNORE_FIR */
|
||||
}
|
||||
Reference in New Issue
Block a user