[AA FIR] implement FirCallableSignature to simplify search by signature

^KT-54826 Fixed
This commit is contained in:
Dmitrii Gridin
2022-11-22 11:14:46 +01:00
committed by Space Team
parent 48e2e5cc87
commit 2155a23e4e
22 changed files with 546 additions and 73 deletions
@@ -9,8 +9,8 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration
import org.jetbrains.kotlin.analysis.api.fir.findPsi
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.FirCallableSignature
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirConstructorSymbolPointer
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.createSignature
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.requireOwnerPointer
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
@@ -37,7 +37,7 @@ internal class KtFirConstructorSymbol(
private val builder: KtSymbolByFirBuilder
) : KtConstructorSymbol(), KtFirSymbol<FirConstructorSymbol> {
override val psi: PsiElement? by cached { firSymbol.findPsi() }
override val returnType: KtType get() = withValidityAssertion { firSymbol.returnType(builder) }
override val valueParameters: List<KtValueParameterSymbol> by cached { firSymbol.createKtValueParameters(builder) }
@@ -49,7 +49,13 @@ internal class KtFirConstructorSymbol(
override val visibility: Visibility get() = withValidityAssertion { firSymbol.visibility }
override val annotationsList by cached { KtFirAnnotationListForDeclaration.create(firSymbol, firResolveSession.useSiteFirSession, token) }
override val annotationsList by cached {
KtFirAnnotationListForDeclaration.create(
firSymbol,
firResolveSession.useSiteFirSession,
token,
)
}
override val containingClassIdIfNonLocal: ClassId?
get() = withValidityAssertion { firSymbol.containingClassLookupTag()?.classId?.takeUnless { it.isLocal } }
@@ -65,7 +71,7 @@ internal class KtFirConstructorSymbol(
throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException("constructor")
}
KtFirConstructorSymbolPointer(requireOwnerPointer(), isPrimary, firSymbol.createSignature())
KtFirConstructorSymbolPointer(requireOwnerPointer(), isPrimary, FirCallableSignature.createSignature(firSymbol))
}
override fun equals(other: Any?): Boolean = symbolEquals(other)
@@ -10,9 +10,9 @@ import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration
import org.jetbrains.kotlin.analysis.api.fir.findPsi
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.FirCallableSignature
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirMemberFunctionSymbolPointer
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirTopLevelFunctionSymbolPointer
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.createSignature
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.requireOwnerPointer
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
import org.jetbrains.kotlin.analysis.api.impl.base.util.kotlinFunctionInvokeCallableIds
@@ -96,11 +96,15 @@ internal class KtFirFunctionSymbol(
KtPsiBasedSymbolPointer.createForSymbolFromSource<KtFunctionSymbol>(this)?.let { return it }
return when (val kind = symbolKind) {
KtSymbolKind.TOP_LEVEL -> KtFirTopLevelFunctionSymbolPointer(firSymbol.callableId, firSymbol.createSignature())
KtSymbolKind.TOP_LEVEL -> KtFirTopLevelFunctionSymbolPointer(
firSymbol.callableId,
FirCallableSignature.createSignature(firSymbol),
)
KtSymbolKind.CLASS_MEMBER -> KtFirMemberFunctionSymbolPointer(
requireOwnerPointer(),
firSymbol.name,
firSymbol.createSignature(),
FirCallableSignature.createSignature(firSymbol),
)
KtSymbolKind.LOCAL -> throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException(
@@ -12,9 +12,9 @@ import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration
import org.jetbrains.kotlin.analysis.api.fir.findPsi
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.FirCallableSignature
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirMemberPropertySymbolPointer
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirTopLevelPropertySymbolPointer
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.createSignature
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.requireOwnerPointer
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
@@ -136,12 +136,16 @@ internal class KtFirKotlinPropertySymbol(
}
return when (val kind = symbolKind) {
KtSymbolKind.TOP_LEVEL -> KtFirTopLevelPropertySymbolPointer(firSymbol.callableId, firSymbol.createSignature())
KtSymbolKind.TOP_LEVEL -> KtFirTopLevelPropertySymbolPointer(
firSymbol.callableId,
FirCallableSignature.createSignature(firSymbol),
)
KtSymbolKind.CLASS_MEMBER ->
KtFirMemberPropertySymbolPointer(
requireOwnerPointer(),
firSymbol.name,
firSymbol.createSignature(),
FirCallableSignature.createSignature(firSymbol),
)
else -> throw UnsupportedSymbolKind(this::class, kind)
@@ -1,11 +1,12 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.fir.symbols
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationsList
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration
@@ -5,26 +5,109 @@
package org.jetbrains.kotlin.analysis.api.fir.symbols.pointers
import org.jetbrains.kotlin.analysis.low.level.api.fir.ideSessionComponents
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.renderer.ConeIdFullRenderer
import org.jetbrains.kotlin.fir.renderer.ConeTypeRenderer
import org.jetbrains.kotlin.fir.renderer.FirRenderer
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.fir.types.FirTypeRef
internal data class FirCallableSignature(private val idSignature: IdSignature) {
fun sameSignature(declaration: FirCallableDeclaration): Boolean = this == declaration.createSignature()
/**
* **Note**: the signature doesn't contain a name. This check should be done externally.
*/
internal class FirCallableSignature private constructor(
private val receiverType: String?,
private val contextReceiverTypes: List<String>,
private val parameters: List<String>?,
private val typeParametersCount: Int,
private val returnType: String,
) {
fun sameSignature(declaration: FirCallableSymbol<*>): Boolean = sameSignature(declaration.fir)
fun sameSignature(declaration: FirCallableDeclaration): Boolean {
if ((receiverType == null) != (declaration.receiverParameter == null)) return false
if (contextReceiverTypes.size != declaration.contextReceivers.size) return false
if (typeParametersCount != declaration.typeParameters.size) return false
if (parameters?.size != (declaration as? FirFunction)?.valueParameters?.size) return false
declaration.lazyResolveToPhase(FirResolvePhase.TYPES)
if (receiverType != declaration.receiverParameter?.typeRef?.renderType()) return false
val receivers = declaration.contextReceivers
for ((index, parameter) in contextReceiverTypes.withIndex()) {
if (receivers[index].typeRef.renderType() != parameter) return false
}
if (declaration is FirFunction) {
requireNotNull(parameters)
for ((index, parameter) in declaration.valueParameters.withIndex()) {
if (parameters[index] != parameter.returnTypeRef.renderType()) return false
}
}
declaration.lazyResolveToPhase(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE)
return returnType == declaration.returnTypeRef.renderType()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FirCallableSignature) return false
if (receiverType != other.receiverType) return false
if (contextReceiverTypes != other.contextReceiverTypes) return false
if (parameters != other.parameters) return false
if (typeParametersCount != other.typeParametersCount) return false
return returnType == other.returnType
}
override fun hashCode(): Int {
var result = receiverType?.hashCode() ?: 0
result = 31 * result + contextReceiverTypes.hashCode()
result = 31 * result + parameters.hashCode()
result = 31 * result + typeParametersCount.hashCode()
result = 31 * result + returnType.hashCode()
return result
}
companion object {
fun createSignature(callableSymbol: FirCallableSymbol<*>): FirCallableSignature = createSignature(callableSymbol.fir)
fun createSignature(callableDeclaration: FirCallableDeclaration): FirCallableSignature {
callableDeclaration.lazyResolveToPhase(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE)
return FirCallableSignature(
receiverType = callableDeclaration.receiverParameter?.typeRef?.renderType(),
contextReceiverTypes = callableDeclaration.contextReceivers.map { it.typeRef.renderType() },
parameters = if (callableDeclaration is FirFunction) {
callableDeclaration.valueParameters.map { it.returnTypeRef.renderType() }
} else {
null
},
typeParametersCount = callableDeclaration.typeParameters.size,
returnType = callableDeclaration.returnTypeRef.renderType(),
)
}
}
}
internal fun FirCallableSymbol<*>.createSignature(): FirCallableSignature = fir.createSignature()
internal fun FirCallableDeclaration.createSignature(): FirCallableSignature {
lazyResolveToPhase(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE)
val signatureComposer = moduleData.session.ideSessionComponents.signatureComposer
return FirCallableSignature(
signatureComposer.composeSignature(this, allowLocalClasses = true)
?: error("Could not compose signature for ${this.renderWithType()}, looks like it is private or local")
)
}
private fun FirTypeRef.renderType(): String = FirRenderer(
annotationRenderer = null,
bodyRenderer = null,
callArgumentsRenderer = null,
classMemberRenderer = null,
contractRenderer = null,
declarationRenderer = null,
idRenderer = ConeIdFullRenderer(),
modifierRenderer = null,
packageDirectiveRenderer = null,
propertyAccessorRenderer = null,
resolvePhaseRenderer = null,
typeRenderer = ConeTypeRenderer(),
valueParameterRenderer = null,
errorExpressionRenderer = null,
).renderElementAsString(this)
@@ -244,6 +244,12 @@ public class FirIdeNormalAnalysisSourceModuleSymbolByPsiTestGenerated extends Ab
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/outerAndInnerTypeAlias.kt");
}
@Test
@TestMetadata("similarGenericSignature.kt")
public void testSimilarGenericSignature() throws Exception {
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/similarGenericSignature.kt");
}
@Test
@TestMetadata("topLevelFunctions.kt")
public void testTopLevelFunctions() throws Exception {
@@ -306,6 +312,12 @@ public class FirIdeNormalAnalysisSourceModuleSymbolByPsiTestGenerated extends Ab
public void testContextReceiversOnProperty() throws Exception {
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/contextReceivers/contextReceiversOnProperty.kt");
}
@Test
@TestMetadata("similarSignatures.kt")
public void testSimilarSignatures() throws Exception {
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/contextReceivers/similarSignatures.kt");
}
}
@Nested