FIR: set missed source in various FirElements

This could be caught by debuggability tests, such as stepping tests,
which are not enabled for FIR yet.
Instead, for now, full pipeline tests will raise index out-of-bound
errors due to the undefined offsets, which stem from null source.
This commit is contained in:
Jinseong Jeon
2020-10-19 12:34:32 -07:00
committed by Mikhail Glukhikh
parent 46cc01602e
commit f4531b0f34
11 changed files with 36 additions and 12 deletions
@@ -30,8 +30,8 @@ import org.jetbrains.kotlin.fir.symbols.AccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.descriptors.WrappedReceiverParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
@@ -46,17 +46,15 @@ import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.OperatorNameConventions
internal fun <T : IrElement> FirElement.convertWithOffsets(
f: (startOffset: Int, endOffset: Int) -> T
): T {
if (psi is PsiCompiledElement) return f(-1, -1)
val startOffset = psi?.startOffsetSkippingComments ?: -1
val endOffset = psi?.endOffset ?: -1
if (psi is PsiCompiledElement) return f(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
val startOffset = psi?.startOffsetSkippingComments ?: UNDEFINED_OFFSET
val endOffset = psi?.endOffset ?: UNDEFINED_OFFSET
return f(startOffset, endOffset)
}
@@ -550,7 +550,11 @@ class Fir2IrVisitor(
return this
}
// TODO: Other conditions to check?
if (expression.typeRef.coneTypeSafe<ConeKotlinType>()?.hasEnhancedNullability != true) {
// [TypeOperatorLowering] will retrieve the source (from start offset to end offset) as an assertion message.
// Avoid type casting if we can't determine the source for some reasons, e.g., implicit `this` receiver.
if (expression.source == null ||
expression.typeRef.coneTypeSafe<ConeKotlinType>()?.hasEnhancedNullability != true
) {
return this
}
return IrTypeOperatorCallImpl(
@@ -1510,6 +1510,7 @@ class DeclarationsConverter(
}
rValue = firExpression!!
dispatchReceiver = buildThisReceiverExpression {
source = firExpression!!.source
calleeReference = buildImplicitThisReference {
boundSymbol = containerSymbol
}
@@ -665,18 +665,21 @@ class ExpressionsConverter(
if (hasSubject) {
val firCondition = entry.toFirWhenCondition()
buildWhenBranch {
source = branch.source
condition = firCondition
result = branch
}
} else {
val firCondition = entry.toFirWhenConditionWithoutSubject()
buildWhenBranch {
source = branch.source
condition = firCondition
result = branch
}
}
} else {
buildWhenBranch {
source = branch.source
condition = buildElseIfTrueCondition()
result = branch
}
@@ -51,8 +51,9 @@ class ValueParameter(
returnTypeRef = type.copyWithNewSourceKind(FirFakeSourceElementKind.PropertyFromParameter)
this.name = name
initializer = buildQualifiedAccessExpression {
source = firValueParameter.source
calleeReference = buildPropertyFromParameterResolvedNamedReference {
this.name = name
this.name = name
resolvedSymbol = this@ValueParameter.firValueParameter.symbol
}
}
@@ -561,11 +561,13 @@ class RawFirBuilder(
source = delegateSource
calleeReference =
buildResolvedNamedReference {
source = delegateSource
name = delegateName
resolvedSymbol = delegateField.symbol
}
rValue = delegateExpression
dispatchReceiver = buildThisReceiverExpression {
source = delegateSource
calleeReference = buildImplicitThisReference {
boundSymbol = containerSymbol
}
@@ -1533,6 +1535,7 @@ class RawFirBuilder(
}
if (expression.elseKeyword != null) {
branches += buildWhenBranch {
source = expression.elseKeyword?.toFirPsiSourceElement()
condition = buildElseIfTrueCondition()
result = expression.`else`.toFirBlock()
}
@@ -1038,6 +1038,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
source = parameterSource
typeRef = firPropertyReturnTypeRefWithCorrectSourceKind
dispatchReceiver = buildThisReceiverExpression {
source = parameterSource
calleeReference = buildImplicitThisReference {
boundSymbol = classBuilder.symbol
}
@@ -1112,6 +1113,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
private fun FirVariable<*>.toQualifiedAccess(): FirQualifiedAccessExpression = buildQualifiedAccessExpression {
calleeReference = buildResolvedNamedReference {
source = this@toQualifiedAccess.source
name = this@toQualifiedAccess.name
resolvedSymbol = this@toQualifiedAccess.symbol
}
@@ -190,7 +190,7 @@ fun FirExpression.generateComparisonExpression(
val compareToCall = createConventionCall(
operationReferenceSource,
baseSource?.fakeElement(FirFakeSourceElementKind.GeneratedCompararisonExpression),
baseSource?.fakeElement(FirFakeSourceElementKind.GeneratedComparisonExpression),
argument,
OperatorNameConventions.COMPARE_TO
)
@@ -229,13 +229,13 @@ private fun FirExpression.createConventionCall(
fun generateAccessExpression(
qualifiedSource: FirSourceElement?,
calleReferenceSource: FirSourceElement?,
calleeReferenceSource: FirSourceElement?,
name: Name
): FirQualifiedAccessExpression =
buildQualifiedAccessExpression {
this.source = qualifiedSource
calleeReference = buildSimpleNamedReference {
this.source = calleReferenceSource
this.source = calleeReferenceSource
this.name = name
}
}
@@ -379,6 +379,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
val annotations = getter?.annotations
val returnTarget = FirFunctionTarget(null, isLambda = false)
getter = buildPropertyAccessor {
this.source = delegateBuilder.source
this.session = session
origin = FirDeclarationOrigin.Source
returnTypeRef = buildImplicitTypeRef()
@@ -410,12 +411,14 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
if (isVar && (setter == null || setter is FirDefaultPropertyAccessor)) {
val annotations = setter?.annotations
setter = buildPropertyAccessor {
this.source = delegateBuilder.source
this.session = session
origin = FirDeclarationOrigin.Source
returnTypeRef = session.builtinTypes.unitType
isGetter = false
status = FirDeclarationStatusImpl(Visibilities.Unknown, Modality.FINAL)
val parameter = buildValueParameter {
source = delegateBuilder.source
this.session = session
origin = FirDeclarationOrigin.Source
returnTypeRef = buildImplicitTypeRef()
@@ -429,6 +432,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
symbol = FirPropertyAccessorSymbol()
body = FirSingleExpressionBlock(
buildFunctionCall {
source = delegateBuilder.source
explicitReceiver = delegateAccess()
calleeReference = buildSimpleNamedReference {
name = SET_VALUE
@@ -438,6 +442,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
arguments += propertyRef()
arguments += buildQualifiedAccessExpression {
calleeReference = buildResolvedNamedReference {
source = delegateBuilder.source
name = DELEGATED_SETTER_PARAM
resolvedSymbol = parameter.symbol
}
@@ -493,6 +498,7 @@ fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression): FirSafeCallExp
this.originalReceiverRef = FirExpressionRef<FirExpression>().apply {
bind(receiver)
}
this.source = receiver.source
}
replaceExplicitReceiver(checkedSafeCallSubject)
@@ -94,6 +94,9 @@ sealed class ImplicitReceiverValue<S : AbstractFirBasedSymbol<*>>(
private fun receiverExpression(symbol: AbstractFirBasedSymbol<*>, type: ConeKotlinType): FirThisReceiverExpression =
buildThisReceiverExpression {
// NB: we can't use `symbol.fir.source` as the source of `this` receiver. For instance, if this is an implicit receiver for a class,
// the entire class itself will be set as a source. If combined with an implicit type operation, a certain assertion, like null
// check assertion, will retrieve source as an assertion message, which is literally the entire class (!).
calleeReference = buildImplicitThisReference {
boundSymbol = symbol
}
@@ -57,6 +57,9 @@ internal fun remapArgumentsWithVararg(
(valueParameter.isVararg && arg !is FirNamedArgumentExpression)
) {
arguments += arg
if (this.source == null) {
this.source = arg.source
}
} else if (arguments.isEmpty()) {
// `arg` is BEFORE the vararg arguments.
newArgumentMapping[arg] = valueParameter
@@ -133,7 +133,7 @@ sealed class FirFakeSourceElementKind : FirSourceElementKind() {
// `a > b` will be wrapped in FirComparisonExpression
// with real source which points to initial `a > b` expression
// and inner FirFunctionCall will refer to a fake source
object GeneratedCompararisonExpression : FirFakeSourceElementKind()
object GeneratedComparisonExpression : FirFakeSourceElementKind()
// a ?: b --> when(val $subj = a) { .... }
// where `val $subj = a` has a fake source