[LL FIR] KTIJ-24895 Record compound assignments with error names

- If the callee reference in a desugared compound assignment was an
  error reference, the compound assignment wasn't cached by
  `FirElementsRecorder`. This led to KTIJ-24895 (see the issue for more
  details).
- The solution falls back on the compound assignment's source to decide
  `isSourceForCompoundAccess`, as an operation name can be reconstructed
  from the PSI.

^KTIJ-24895 fixed
This commit is contained in:
Marco Pennekamp
2023-03-20 18:02:29 +01:00
committed by Space Team
parent d14a55d775
commit 9a3eece944
2 changed files with 32 additions and 5 deletions
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.DuplicatedFirSourceElementsException
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.isErrorElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.builder.toFirOperationOrNull
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.references.*
@@ -19,6 +20,7 @@ import org.jetbrains.kotlin.fir.types.FirUserTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
import org.jetbrains.kotlin.types.ConstantValueKind
@@ -147,17 +149,33 @@ internal open class FirElementsRecorder : FirVisitor<Unit, MutableMap<KtElement,
private fun FirElement.isReadInCompoundCall(): Boolean {
if (this is FirPropertyAccessExpression) return true
if (this !is FirFunctionCall) return false
val name = (calleeReference as? FirResolvedNamedReference)?.name
val name = (calleeReference as? FirResolvedNamedReference)?.name ?: getFallbackCompoundCalleeName()
return name == OperatorNameConventions.GET
}
private fun FirElement.isWriteInCompoundCall(): Boolean {
if (this is FirVariableAssignment) return true
if (this !is FirFunctionCall) return false
val name = (calleeReference as? FirResolvedNamedReference)?.name
val name = (calleeReference as? FirResolvedNamedReference)?.name ?: getFallbackCompoundCalleeName()
return name == OperatorNameConventions.SET || name in OperatorNameConventions.ASSIGNMENT_OPERATIONS
}
/**
* If the callee reference is not a [FirResolvedNamedReference], we can get the compound callee name from the source instead. For
* example, if the callee reference is a [FirErrorNamedReference] with an unresolved name `plusAssign`, the operation element type from
* the source will be `KtTokens.PLUSEQ`, which can be transformed to `plusAssign`.
*/
private fun FirElement.getFallbackCompoundCalleeName(): Name? {
val psi = source.psi as? KtOperationExpression ?: return null
val operationReference = psi.operationReference
return operationReference.getAssignmentOperationName() ?: operationReference.getReferencedNameAsName()
}
private fun KtSimpleNameExpression.getAssignmentOperationName(): Name? {
val firOperation = getReferencedNameElementType().toFirOperationOrNull() ?: return null
return FirOperationNameConventions.ASSIGNMENTS[firOperation]
}
private val FirConstExpression<*>.isConverted: Boolean
get() {
val firSourcePsi = this.source?.psi ?: return false
@@ -34,8 +34,14 @@ import org.jetbrains.kotlin.fir.references.builder.buildDelegateFieldReference
import org.jetbrains.kotlin.fir.references.builder.buildImplicitThisReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirDelegateFieldSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeStarProjection
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.*
import org.jetbrains.kotlin.lexer.KtTokens
@@ -141,6 +147,9 @@ fun IElementType.toUnaryName(): Name? {
}
fun IElementType.toFirOperation(): FirOperation =
toFirOperationOrNull() ?: error("Cannot convert element type to FIR operation: $this")
fun IElementType.toFirOperationOrNull(): FirOperation? =
when (this) {
KtTokens.LT -> FirOperation.LT
KtTokens.GT -> FirOperation.GT
@@ -161,7 +170,7 @@ fun IElementType.toFirOperation(): FirOperation =
KtTokens.AS_KEYWORD -> FirOperation.AS
KtTokens.AS_SAFE -> FirOperation.SAFE_AS
else -> throw AssertionError(this.toString())
else -> null
}
fun FirExpression.generateNotNullOrOther(