FIR: fix atomic qualified acess sharing the same PSI with callee ref
Consider an atomic reference `i`, it's FIR representation is - FirQualifiedAccessExpression - calleeReference : FirNamedReference Currently, both of the above FIR elements uses the same PSI element as the source. Such problems are not present with `this` or `super` because we manually mark them as `ExplicitThisOrSuperReference`. This change generalizes the previous `ExplicitThisOrSuperReference` as `ReferenceInAtomicQualifiedAccess` and fixes it for more cases.
This commit is contained in:
committed by
Ivan Kochurkin
parent
3bc0eaff59
commit
45d31fdba2
+2
-1
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||||
|
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
|
||||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
@@ -18,7 +19,7 @@ fun checkUnderscoreDiagnostics(
|
|||||||
reporter: DiagnosticReporter,
|
reporter: DiagnosticReporter,
|
||||||
isExpression: Boolean
|
isExpression: Boolean
|
||||||
) {
|
) {
|
||||||
if (source != null && source.kind !is FirFakeSourceElementKind) {
|
if (source != null && (source.kind is FirRealSourceElementKind || source.kind is FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)) {
|
||||||
with(SourceNavigator.forSource(source)) {
|
with(SourceNavigator.forSource(source)) {
|
||||||
if (source.getRawIdentifier()?.isUnderscore == true) {
|
if (source.getRawIdentifier()?.isUnderscore == true) {
|
||||||
reporter.reportOn(
|
reporter.reportOn(
|
||||||
|
|||||||
+2
-2
@@ -77,8 +77,8 @@ private fun ConeDiagnostic.toFirDiagnostic(
|
|||||||
FirErrors.OUTER_CLASS_ARGUMENTS_REQUIRED.createOn(qualifiedAccessSource ?: source, this.symbol)
|
FirErrors.OUTER_CLASS_ARGUMENTS_REQUIRED.createOn(qualifiedAccessSource ?: source, this.symbol)
|
||||||
is ConeNoTypeArgumentsOnRhsError ->
|
is ConeNoTypeArgumentsOnRhsError ->
|
||||||
FirErrors.NO_TYPE_ARGUMENTS_ON_RHS.createOn(qualifiedAccessSource ?: source, this.desiredCount, this.type)
|
FirErrors.NO_TYPE_ARGUMENTS_ON_RHS.createOn(qualifiedAccessSource ?: source, this.desiredCount, this.type)
|
||||||
is ConeSimpleDiagnostic -> when (source.kind) {
|
is ConeSimpleDiagnostic -> when {
|
||||||
is FirFakeSourceElementKind -> null
|
source.kind is FirFakeSourceElementKind && source.kind != FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess -> null
|
||||||
else -> this.getFactory(source).createOn(qualifiedAccessSource ?: source)
|
else -> this.getFactory(source).createOn(qualifiedAccessSource ?: source)
|
||||||
}
|
}
|
||||||
is ConeInstanceAccessBeforeSuperCall -> FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.createOn(source, this.target)
|
is ConeInstanceAccessBeforeSuperCall -> FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.createOn(source, this.target)
|
||||||
|
|||||||
+21
-11
@@ -468,7 +468,7 @@ class ExpressionsConverter(
|
|||||||
var isReceiver = true
|
var isReceiver = true
|
||||||
var hasQuestionMarkAtLHS = false
|
var hasQuestionMarkAtLHS = false
|
||||||
var firReceiverExpression: FirExpression? = null
|
var firReceiverExpression: FirExpression? = null
|
||||||
lateinit var firCallableReference: FirQualifiedAccess
|
lateinit var namedReference: FirNamedReference
|
||||||
callableReferenceExpression.forEachChildren {
|
callableReferenceExpression.forEachChildren {
|
||||||
when (it.tokenType) {
|
when (it.tokenType) {
|
||||||
COLONCOLON -> isReceiver = false
|
COLONCOLON -> isReceiver = false
|
||||||
@@ -477,7 +477,7 @@ class ExpressionsConverter(
|
|||||||
if (isReceiver) {
|
if (isReceiver) {
|
||||||
firReceiverExpression = getAsFirExpression(it, "Incorrect receiver expression")
|
firReceiverExpression = getAsFirExpression(it, "Incorrect receiver expression")
|
||||||
} else {
|
} else {
|
||||||
firCallableReference = convertSimpleNameExpression(it)
|
namedReference = createSimpleNamedReference(it.toFirSourceElement(), it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,7 +485,7 @@ class ExpressionsConverter(
|
|||||||
|
|
||||||
return buildCallableReferenceAccess {
|
return buildCallableReferenceAccess {
|
||||||
source = callableReferenceExpression.toFirSourceElement()
|
source = callableReferenceExpression.toFirSourceElement()
|
||||||
calleeReference = firCallableReference.calleeReference as FirNamedReference
|
calleeReference = namedReference
|
||||||
explicitReceiver = firReceiverExpression
|
explicitReceiver = firReceiverExpression
|
||||||
this.hasQuestionMarkAtLHS = hasQuestionMarkAtLHS
|
this.hasQuestionMarkAtLHS = hasQuestionMarkAtLHS
|
||||||
}
|
}
|
||||||
@@ -965,19 +965,29 @@ class ExpressionsConverter(
|
|||||||
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitSimpleNameExpression
|
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitSimpleNameExpression
|
||||||
*/
|
*/
|
||||||
private fun convertSimpleNameExpression(referenceExpression: LighterASTNode): FirQualifiedAccessExpression {
|
private fun convertSimpleNameExpression(referenceExpression: LighterASTNode): FirQualifiedAccessExpression {
|
||||||
|
val nameSource = referenceExpression.toFirSourceElement()
|
||||||
|
val referenceSourceElement = if (nameSource.kind is FirFakeSourceElementKind) {
|
||||||
|
nameSource
|
||||||
|
} else {
|
||||||
|
nameSource.fakeElement(FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
|
||||||
|
}
|
||||||
return buildQualifiedAccessExpression {
|
return buildQualifiedAccessExpression {
|
||||||
source = referenceExpression.toFirSourceElement()
|
|
||||||
|
|
||||||
val nameSource = this@buildQualifiedAccessExpression.source
|
|
||||||
val rawText = referenceExpression.asText
|
val rawText = referenceExpression.asText
|
||||||
if (nameSource != null && rawText.isUnderscore) {
|
if (rawText.isUnderscore) {
|
||||||
nonFatalDiagnostics.add(ConeUnderscoreUsageWithoutBackticks(nameSource))
|
nonFatalDiagnostics.add(ConeUnderscoreUsageWithoutBackticks(nameSource))
|
||||||
}
|
}
|
||||||
|
source = nameSource
|
||||||
|
calleeReference = createSimpleNamedReference(referenceSourceElement, referenceExpression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
calleeReference = buildSimpleNamedReference {
|
private fun createSimpleNamedReference(
|
||||||
source = nameSource
|
sourceElement: FirSourceElement,
|
||||||
name = rawText.nameAsSafeName()
|
referenceExpression: LighterASTNode
|
||||||
}
|
): FirNamedReference {
|
||||||
|
return buildSimpleNamedReference {
|
||||||
|
source = sourceElement
|
||||||
|
name = referenceExpression.asText.nameAsSafeName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2222,7 +2222,7 @@ open class RawFirBuilder(
|
|||||||
val sourceElement = expression.toFirSourceElement()
|
val sourceElement = expression.toFirSourceElement()
|
||||||
source = sourceElement
|
source = sourceElement
|
||||||
calleeReference = buildExplicitThisReference {
|
calleeReference = buildExplicitThisReference {
|
||||||
source = sourceElement.fakeElement(FirFakeSourceElementKind.ExplicitThisOrSuperReference)
|
source = sourceElement.fakeElement(FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
|
||||||
labelName = expression.getLabelName()
|
labelName = expression.getLabelName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2234,7 +2234,7 @@ open class RawFirBuilder(
|
|||||||
return buildQualifiedAccessExpression {
|
return buildQualifiedAccessExpression {
|
||||||
this.source = theSource
|
this.source = theSource
|
||||||
calleeReference = buildExplicitSuperReference {
|
calleeReference = buildExplicitSuperReference {
|
||||||
source = theSource.fakeElement(FirFakeSourceElementKind.ExplicitThisOrSuperReference)
|
source = theSource.fakeElement(FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
|
||||||
labelName = expression.getLabelName()
|
labelName = expression.getLabelName()
|
||||||
superTypeRef = superType.toFirOrImplicitType()
|
superTypeRef = superType.toFirOrImplicitType()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1183,7 +1183,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
|
source = this@toQualifiedAccess.source?.fakeElement(FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
|
||||||
name = this@toQualifiedAccess.name
|
name = this@toQualifiedAccess.name
|
||||||
resolvedSymbol = this@toQualifiedAccess.symbol
|
resolvedSymbol = this@toQualifiedAccess.symbol
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -257,7 +257,10 @@ fun generateAccessExpression(
|
|||||||
buildQualifiedAccessExpression {
|
buildQualifiedAccessExpression {
|
||||||
this.source = qualifiedSource
|
this.source = qualifiedSource
|
||||||
calleeReference = buildSimpleNamedReference {
|
calleeReference = buildSimpleNamedReference {
|
||||||
this.source = calleeReferenceSource
|
this.source = if (calleeReferenceSource == qualifiedSource)
|
||||||
|
calleeReferenceSource?.fakeElement(FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
|
||||||
|
else
|
||||||
|
calleeReferenceSource
|
||||||
this.name = name
|
this.name = name
|
||||||
}
|
}
|
||||||
if (diagnostic != null) {
|
if (diagnostic != null) {
|
||||||
|
|||||||
@@ -83,9 +83,10 @@ sealed class FirFakeSourceElementKind : FirSourceElementKind() {
|
|||||||
|
|
||||||
object ImplicitInvokeCall : FirFakeSourceElementKind()
|
object ImplicitInvokeCall : FirFakeSourceElementKind()
|
||||||
|
|
||||||
// this/super expressions have FirThisReference/FirSuperReference
|
// Consider an atomic qualified access like `i`. In the FIR tree, both the FirQualifiedAccessExpression and its calleeReference uses
|
||||||
// with a fake sources which refers to this this/super expression
|
// `i` as the source. Hence, this fake kind is set on the `calleeReference` to make sure no PSI element is shared by multiple FIR
|
||||||
object ExplicitThisOrSuperReference : FirFakeSourceElementKind()
|
// elements. This also applies to `this` and `super` references.
|
||||||
|
object ReferenceInAtomicQualifiedAccess : FirFakeSourceElementKind()
|
||||||
|
|
||||||
// for enum classes we have valueOf & values functions generated
|
// for enum classes we have valueOf & values functions generated
|
||||||
// with a fake sources which refers to this the enum class
|
// with a fake sources which refers to this the enum class
|
||||||
@@ -266,13 +267,46 @@ sealed class FirPsiSourceElement(val psi: PsiElement) : FirSourceElement() {
|
|||||||
return element.textRange.endOffset
|
return element.textRange.endOffset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as FirPsiSourceElement
|
||||||
|
|
||||||
|
if (psi != other.psi) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return psi.hashCode()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FirRealPsiSourceElement(psi: PsiElement) : FirPsiSourceElement(psi) {
|
class FirRealPsiSourceElement(psi: PsiElement) : FirPsiSourceElement(psi) {
|
||||||
override val kind: FirSourceElementKind get() = FirRealSourceElementKind
|
override val kind: FirSourceElementKind get() = FirRealSourceElementKind
|
||||||
}
|
}
|
||||||
|
|
||||||
class FirFakeSourceElement(psi: PsiElement, override val kind: FirFakeSourceElementKind) : FirPsiSourceElement(psi)
|
class FirFakeSourceElement(psi: PsiElement, override val kind: FirFakeSourceElementKind) : FirPsiSourceElement(psi) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
if (!super.equals(other)) return false
|
||||||
|
|
||||||
|
other as FirFakeSourceElement
|
||||||
|
|
||||||
|
if (kind != other.kind) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = super.hashCode()
|
||||||
|
result = 31 * result + kind.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun FirSourceElement.fakeElement(newKind: FirFakeSourceElementKind): FirSourceElement {
|
fun FirSourceElement.fakeElement(newKind: FirFakeSourceElementKind): FirSourceElement {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
@@ -310,6 +344,30 @@ class FirLightSourceElement(
|
|||||||
val node = treeStructure.unwrap(lighterASTNode)
|
val node = treeStructure.unwrap(lighterASTNode)
|
||||||
return node.psi?.toFirPsiSourceElement(kind)
|
return node.psi?.toFirPsiSourceElement(kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as FirLightSourceElement
|
||||||
|
|
||||||
|
if (lighterASTNode != other.lighterASTNode) return false
|
||||||
|
if (startOffset != other.startOffset) return false
|
||||||
|
if (endOffset != other.endOffset) return false
|
||||||
|
if (treeStructure != other.treeStructure) return false
|
||||||
|
if (kind != other.kind) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = lighterASTNode.hashCode()
|
||||||
|
result = 31 * result + startOffset
|
||||||
|
result = 31 * result + endOffset
|
||||||
|
result = 31 * result + treeStructure.hashCode()
|
||||||
|
result = 31 * result + kind.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val FirSourceElement?.psi: PsiElement? get() = (this as? FirPsiSourceElement)?.psi
|
val FirSourceElement?.psi: PsiElement? get() = (this as? FirPsiSourceElement)?.psi
|
||||||
|
|||||||
+1
@@ -52,6 +52,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
|
|||||||
private val allowedKindsForDebugInfo = setOf(
|
private val allowedKindsForDebugInfo = setOf(
|
||||||
FirRealSourceElementKind,
|
FirRealSourceElementKind,
|
||||||
FirFakeSourceElementKind.DesugaredCompoundAssignment,
|
FirFakeSourceElementKind.DesugaredCompoundAssignment,
|
||||||
|
FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -41,6 +41,7 @@ internal class KtToFirMapping(firElement: FirElement, recorder: FirElementsRecor
|
|||||||
|
|
||||||
private val userTypeMapping = ConcurrentHashMap<KtUserType, FirElement>()
|
private val userTypeMapping = ConcurrentHashMap<KtUserType, FirElement>()
|
||||||
fun getElement(ktElement: KtElement, state: FirModuleResolveState): FirElement? {
|
fun getElement(ktElement: KtElement, state: FirModuleResolveState): FirElement? {
|
||||||
|
|
||||||
mapping[ktElement]?.let { return it }
|
mapping[ktElement]?.let { return it }
|
||||||
|
|
||||||
val userType = when (ktElement) {
|
val userType = when (ktElement) {
|
||||||
|
|||||||
+9
-4
@@ -6,8 +6,10 @@
|
|||||||
package org.jetbrains.kotlin.idea.fir.low.level.api.file.structure
|
package org.jetbrains.kotlin.idea.fir.low.level.api.file.structure
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||||
|
import org.jetbrains.kotlin.fir.FirRealPsiSourceElement
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||||
import org.jetbrains.kotlin.fir.realPsi
|
import org.jetbrains.kotlin.fir.psi
|
||||||
import org.jetbrains.kotlin.fir.references.*
|
import org.jetbrains.kotlin.fir.references.*
|
||||||
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
|
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
|
||||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||||
@@ -90,9 +92,12 @@ internal open class FirElementsRecorder : FirVisitor<Unit, MutableMap<KtElement,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun cacheElement(element: FirElement, cache: MutableMap<KtElement, FirElement>) {
|
private fun cacheElement(element: FirElement, cache: MutableMap<KtElement, FirElement>) {
|
||||||
(element.realPsi as? KtElement)?.let { psi ->
|
val psi = element.source
|
||||||
cache(psi, element, cache)
|
?.takeIf {
|
||||||
}
|
it.kind == FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess || it is FirRealPsiSourceElement
|
||||||
|
}.psi as? KtElement
|
||||||
|
?: return
|
||||||
|
cache(psi, element, cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
Reference in New Issue
Block a user