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.symbols.impl.*
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.* 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.descriptors.WrappedReceiverParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind 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.name.Name
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments 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.types.Variance
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
internal fun <T : IrElement> FirElement.convertWithOffsets( internal fun <T : IrElement> FirElement.convertWithOffsets(
f: (startOffset: Int, endOffset: Int) -> T f: (startOffset: Int, endOffset: Int) -> T
): T { ): T {
if (psi is PsiCompiledElement) return f(-1, -1) if (psi is PsiCompiledElement) return f(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
val startOffset = psi?.startOffsetSkippingComments ?: -1 val startOffset = psi?.startOffsetSkippingComments ?: UNDEFINED_OFFSET
val endOffset = psi?.endOffset ?: -1 val endOffset = psi?.endOffset ?: UNDEFINED_OFFSET
return f(startOffset, endOffset) return f(startOffset, endOffset)
} }
@@ -550,7 +550,11 @@ class Fir2IrVisitor(
return this return this
} }
// TODO: Other conditions to check? // 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 this
} }
return IrTypeOperatorCallImpl( return IrTypeOperatorCallImpl(
@@ -1510,6 +1510,7 @@ class DeclarationsConverter(
} }
rValue = firExpression!! rValue = firExpression!!
dispatchReceiver = buildThisReceiverExpression { dispatchReceiver = buildThisReceiverExpression {
source = firExpression!!.source
calleeReference = buildImplicitThisReference { calleeReference = buildImplicitThisReference {
boundSymbol = containerSymbol boundSymbol = containerSymbol
} }
@@ -665,18 +665,21 @@ class ExpressionsConverter(
if (hasSubject) { if (hasSubject) {
val firCondition = entry.toFirWhenCondition() val firCondition = entry.toFirWhenCondition()
buildWhenBranch { buildWhenBranch {
source = branch.source
condition = firCondition condition = firCondition
result = branch result = branch
} }
} else { } else {
val firCondition = entry.toFirWhenConditionWithoutSubject() val firCondition = entry.toFirWhenConditionWithoutSubject()
buildWhenBranch { buildWhenBranch {
source = branch.source
condition = firCondition condition = firCondition
result = branch result = branch
} }
} }
} else { } else {
buildWhenBranch { buildWhenBranch {
source = branch.source
condition = buildElseIfTrueCondition() condition = buildElseIfTrueCondition()
result = branch result = branch
} }
@@ -51,8 +51,9 @@ class ValueParameter(
returnTypeRef = type.copyWithNewSourceKind(FirFakeSourceElementKind.PropertyFromParameter) returnTypeRef = type.copyWithNewSourceKind(FirFakeSourceElementKind.PropertyFromParameter)
this.name = name this.name = name
initializer = buildQualifiedAccessExpression { initializer = buildQualifiedAccessExpression {
source = firValueParameter.source
calleeReference = buildPropertyFromParameterResolvedNamedReference { calleeReference = buildPropertyFromParameterResolvedNamedReference {
this.name = name this.name = name
resolvedSymbol = this@ValueParameter.firValueParameter.symbol resolvedSymbol = this@ValueParameter.firValueParameter.symbol
} }
} }
@@ -561,11 +561,13 @@ class RawFirBuilder(
source = delegateSource source = delegateSource
calleeReference = calleeReference =
buildResolvedNamedReference { buildResolvedNamedReference {
source = delegateSource
name = delegateName name = delegateName
resolvedSymbol = delegateField.symbol resolvedSymbol = delegateField.symbol
} }
rValue = delegateExpression rValue = delegateExpression
dispatchReceiver = buildThisReceiverExpression { dispatchReceiver = buildThisReceiverExpression {
source = delegateSource
calleeReference = buildImplicitThisReference { calleeReference = buildImplicitThisReference {
boundSymbol = containerSymbol boundSymbol = containerSymbol
} }
@@ -1533,6 +1535,7 @@ class RawFirBuilder(
} }
if (expression.elseKeyword != null) { if (expression.elseKeyword != null) {
branches += buildWhenBranch { branches += buildWhenBranch {
source = expression.elseKeyword?.toFirPsiSourceElement()
condition = buildElseIfTrueCondition() condition = buildElseIfTrueCondition()
result = expression.`else`.toFirBlock() result = expression.`else`.toFirBlock()
} }
@@ -1038,6 +1038,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
source = parameterSource source = parameterSource
typeRef = firPropertyReturnTypeRefWithCorrectSourceKind typeRef = firPropertyReturnTypeRefWithCorrectSourceKind
dispatchReceiver = buildThisReceiverExpression { dispatchReceiver = buildThisReceiverExpression {
source = parameterSource
calleeReference = buildImplicitThisReference { calleeReference = buildImplicitThisReference {
boundSymbol = classBuilder.symbol boundSymbol = classBuilder.symbol
} }
@@ -1112,6 +1113,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
private fun FirVariable<*>.toQualifiedAccess(): FirQualifiedAccessExpression = buildQualifiedAccessExpression { private fun FirVariable<*>.toQualifiedAccess(): FirQualifiedAccessExpression = buildQualifiedAccessExpression {
calleeReference = buildResolvedNamedReference { calleeReference = buildResolvedNamedReference {
source = this@toQualifiedAccess.source
name = this@toQualifiedAccess.name name = this@toQualifiedAccess.name
resolvedSymbol = this@toQualifiedAccess.symbol resolvedSymbol = this@toQualifiedAccess.symbol
} }
@@ -190,7 +190,7 @@ fun FirExpression.generateComparisonExpression(
val compareToCall = createConventionCall( val compareToCall = createConventionCall(
operationReferenceSource, operationReferenceSource,
baseSource?.fakeElement(FirFakeSourceElementKind.GeneratedCompararisonExpression), baseSource?.fakeElement(FirFakeSourceElementKind.GeneratedComparisonExpression),
argument, argument,
OperatorNameConventions.COMPARE_TO OperatorNameConventions.COMPARE_TO
) )
@@ -229,13 +229,13 @@ private fun FirExpression.createConventionCall(
fun generateAccessExpression( fun generateAccessExpression(
qualifiedSource: FirSourceElement?, qualifiedSource: FirSourceElement?,
calleReferenceSource: FirSourceElement?, calleeReferenceSource: FirSourceElement?,
name: Name name: Name
): FirQualifiedAccessExpression = ): FirQualifiedAccessExpression =
buildQualifiedAccessExpression { buildQualifiedAccessExpression {
this.source = qualifiedSource this.source = qualifiedSource
calleeReference = buildSimpleNamedReference { calleeReference = buildSimpleNamedReference {
this.source = calleReferenceSource this.source = calleeReferenceSource
this.name = name this.name = name
} }
} }
@@ -379,6 +379,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
val annotations = getter?.annotations val annotations = getter?.annotations
val returnTarget = FirFunctionTarget(null, isLambda = false) val returnTarget = FirFunctionTarget(null, isLambda = false)
getter = buildPropertyAccessor { getter = buildPropertyAccessor {
this.source = delegateBuilder.source
this.session = session this.session = session
origin = FirDeclarationOrigin.Source origin = FirDeclarationOrigin.Source
returnTypeRef = buildImplicitTypeRef() returnTypeRef = buildImplicitTypeRef()
@@ -410,12 +411,14 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
if (isVar && (setter == null || setter is FirDefaultPropertyAccessor)) { if (isVar && (setter == null || setter is FirDefaultPropertyAccessor)) {
val annotations = setter?.annotations val annotations = setter?.annotations
setter = buildPropertyAccessor { setter = buildPropertyAccessor {
this.source = delegateBuilder.source
this.session = session this.session = session
origin = FirDeclarationOrigin.Source origin = FirDeclarationOrigin.Source
returnTypeRef = session.builtinTypes.unitType returnTypeRef = session.builtinTypes.unitType
isGetter = false isGetter = false
status = FirDeclarationStatusImpl(Visibilities.Unknown, Modality.FINAL) status = FirDeclarationStatusImpl(Visibilities.Unknown, Modality.FINAL)
val parameter = buildValueParameter { val parameter = buildValueParameter {
source = delegateBuilder.source
this.session = session this.session = session
origin = FirDeclarationOrigin.Source origin = FirDeclarationOrigin.Source
returnTypeRef = buildImplicitTypeRef() returnTypeRef = buildImplicitTypeRef()
@@ -429,6 +432,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
symbol = FirPropertyAccessorSymbol() symbol = FirPropertyAccessorSymbol()
body = FirSingleExpressionBlock( body = FirSingleExpressionBlock(
buildFunctionCall { buildFunctionCall {
source = delegateBuilder.source
explicitReceiver = delegateAccess() explicitReceiver = delegateAccess()
calleeReference = buildSimpleNamedReference { calleeReference = buildSimpleNamedReference {
name = SET_VALUE name = SET_VALUE
@@ -438,6 +442,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
arguments += propertyRef() arguments += propertyRef()
arguments += buildQualifiedAccessExpression { arguments += buildQualifiedAccessExpression {
calleeReference = buildResolvedNamedReference { calleeReference = buildResolvedNamedReference {
source = delegateBuilder.source
name = DELEGATED_SETTER_PARAM name = DELEGATED_SETTER_PARAM
resolvedSymbol = parameter.symbol resolvedSymbol = parameter.symbol
} }
@@ -493,6 +498,7 @@ fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression): FirSafeCallExp
this.originalReceiverRef = FirExpressionRef<FirExpression>().apply { this.originalReceiverRef = FirExpressionRef<FirExpression>().apply {
bind(receiver) bind(receiver)
} }
this.source = receiver.source
} }
replaceExplicitReceiver(checkedSafeCallSubject) replaceExplicitReceiver(checkedSafeCallSubject)
@@ -94,6 +94,9 @@ sealed class ImplicitReceiverValue<S : AbstractFirBasedSymbol<*>>(
private fun receiverExpression(symbol: AbstractFirBasedSymbol<*>, type: ConeKotlinType): FirThisReceiverExpression = private fun receiverExpression(symbol: AbstractFirBasedSymbol<*>, type: ConeKotlinType): FirThisReceiverExpression =
buildThisReceiverExpression { 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 { calleeReference = buildImplicitThisReference {
boundSymbol = symbol boundSymbol = symbol
} }
@@ -57,6 +57,9 @@ internal fun remapArgumentsWithVararg(
(valueParameter.isVararg && arg !is FirNamedArgumentExpression) (valueParameter.isVararg && arg !is FirNamedArgumentExpression)
) { ) {
arguments += arg arguments += arg
if (this.source == null) {
this.source = arg.source
}
} else if (arguments.isEmpty()) { } else if (arguments.isEmpty()) {
// `arg` is BEFORE the vararg arguments. // `arg` is BEFORE the vararg arguments.
newArgumentMapping[arg] = valueParameter newArgumentMapping[arg] = valueParameter
@@ -133,7 +133,7 @@ sealed class FirFakeSourceElementKind : FirSourceElementKind() {
// `a > b` will be wrapped in FirComparisonExpression // `a > b` will be wrapped in FirComparisonExpression
// with real source which points to initial `a > b` expression // with real source which points to initial `a > b` expression
// and inner FirFunctionCall will refer to a fake source // and inner FirFunctionCall will refer to a fake source
object GeneratedCompararisonExpression : FirFakeSourceElementKind() object GeneratedComparisonExpression : FirFakeSourceElementKind()
// a ?: b --> when(val $subj = a) { .... } // a ?: b --> when(val $subj = a) { .... }
// where `val $subj = a` has a fake source // where `val $subj = a` has a fake source