[Analysis API FIR] Initialize properties of `KtCallableSignature` lazily
diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CallResolver.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CallResolver.kt
index 01053b6..8812364 100644
--- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CallResolver.kt
+++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CallResolver.kt
@@ -10,6 +10,8 @@
 import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade.AnalysisMode
 import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
 import org.jetbrains.kotlin.analysis.api.descriptors.components.base.Fe10KtAnalysisSessionComponent
+import org.jetbrains.kotlin.analysis.api.descriptors.signatures.KtFe10FunctionLikeSignature
+import org.jetbrains.kotlin.analysis.api.descriptors.signatures.KtFe10VariableLikeSignature
 import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.KtFe10DescValueParameterSymbol
 import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.KtFe10ReceiverParameterSymbol
 import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.KtFe10DescSymbol
@@ -59,10 +61,6 @@
 import org.jetbrains.kotlin.types.KotlinType
 import org.jetbrains.kotlin.types.TypeUtils
 import org.jetbrains.kotlin.types.UnwrappedType
-import org.jetbrains.kotlin.types.asSimpleType
-import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.contains
-import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.isTypeVariable
-import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.typeConstructor
 import org.jetbrains.kotlin.types.expressions.OperatorConventions
 import org.jetbrains.kotlin.types.typeUtil.contains
 import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -567,8 +565,8 @@
             resultingDescriptor.extensionReceiverParameter?.returnType?.toKtType(analysisContext)
         }
         return when (symbol) {
-            is KtVariableLikeSymbol -> KtVariableLikeSignature(symbol, ktReturnType, receiverType)
-            is KtFunctionLikeSymbol -> KtFunctionLikeSignature(
+            is KtVariableLikeSymbol -> KtFe10VariableLikeSignature(symbol, ktReturnType, receiverType)
+            is KtFunctionLikeSymbol -> KtFe10FunctionLikeSignature(
                 symbol,
                 ktReturnType,
                 receiverType,
diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SignatureSubstitutor.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SignatureSubstitutor.kt
index 5aa2977..c550c2c 100644
--- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SignatureSubstitutor.kt
+++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10SignatureSubstitutor.kt
@@ -7,8 +7,23 @@
 
 import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
 import org.jetbrains.kotlin.analysis.api.descriptors.components.base.Fe10KtAnalysisSessionComponent
-import org.jetbrains.kotlin.analysis.api.impl.base.components.AbstractKtSignatureSubstitutorImpl
+import org.jetbrains.kotlin.analysis.api.descriptors.signatures.KtFe10FunctionLikeSignature
+import org.jetbrains.kotlin.analysis.api.descriptors.signatures.KtFe10VariableLikeSignature
+import org.jetbrains.kotlin.analysis.api.impl.base.components.AbstractKtSignatureSubstitutor
+import org.jetbrains.kotlin.analysis.api.signatures.KtFunctionLikeSignature
+import org.jetbrains.kotlin.analysis.api.signatures.KtVariableLikeSignature
+import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.KtVariableLikeSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.receiverType
 
 internal class KtFe10SignatureSubstitutor(
     override val analysisSession: KtFe10AnalysisSession
-) : AbstractKtSignatureSubstitutorImpl(), Fe10KtAnalysisSessionComponent
\ No newline at end of file
+) : AbstractKtSignatureSubstitutor(), Fe10KtAnalysisSessionComponent {
+    override fun <S : KtFunctionLikeSymbol> asSignature(symbol: S): KtFunctionLikeSignature<S> {
+        return KtFe10FunctionLikeSignature(symbol, symbol.returnType, symbol.receiverType, symbol.valueParameters.map { asSignature(it) })
+    }
+
+    override fun <S : KtVariableLikeSymbol> asSignature(symbol: S): KtVariableLikeSignature<S> {
+        return KtFe10VariableLikeSignature(symbol, symbol.returnType, symbol.receiverType)
+    }
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/signatures/KtFe10FunctionLikeSignature.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/signatures/KtFe10FunctionLikeSignature.kt
new file mode 100644
index 0000000..5789ee7
--- /dev/null
+++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/signatures/KtFe10FunctionLikeSignature.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010-2023 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.descriptors.signatures
+
+import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
+import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
+import org.jetbrains.kotlin.analysis.api.signatures.KtFunctionLikeSignature
+import org.jetbrains.kotlin.analysis.api.signatures.KtVariableLikeSignature
+import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
+import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
+import org.jetbrains.kotlin.analysis.api.types.KtType
+
+internal class KtFe10FunctionLikeSignature<out S : KtFunctionLikeSymbol>(
+    private val _symbol: S,
+    private val _returnType: KtType,
+    private val _receiverType: KtType?,
+    private val _valueParameters: List<KtVariableLikeSignature<KtValueParameterSymbol>>,
+) : KtFunctionLikeSignature<S>() {
+    override val token: KtLifetimeToken
+        get() = _symbol.token
+    override val symbol: S
+        get() = withValidityAssertion { _symbol }
+    override val returnType: KtType
+        get() = withValidityAssertion { _returnType }
+    override val receiverType: KtType?
+        get() = withValidityAssertion { _receiverType }
+    override val valueParameters: List<KtVariableLikeSignature<KtValueParameterSymbol>>
+        get() = withValidityAssertion { _valueParameters }
+
+    override fun substitute(substitutor: KtSubstitutor): KtFunctionLikeSignature<S> = withValidityAssertion {
+        KtFe10FunctionLikeSignature(
+            symbol,
+            substitutor.substitute(returnType),
+            receiverType?.let { substitutor.substitute(it) },
+            valueParameters.map { valueParameter ->
+                KtFe10VariableLikeSignature<KtValueParameterSymbol>(
+                    valueParameter.symbol,
+                    substitutor.substitute(valueParameter.returnType),
+                    valueParameter.receiverType?.let { substitutor.substitute(it) }
+                )
+            }
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as KtFunctionLikeSignature<*>
+
+        if (symbol != other.symbol) return false
+        if (returnType != other.returnType) return false
+        if (receiverType != other.receiverType) return false
+        if (valueParameters != other.valueParameters) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = symbol.hashCode()
+        result = 31 * result + returnType.hashCode()
+        result = 31 * result + (receiverType?.hashCode() ?: 0)
+        result = 31 * result + valueParameters.hashCode()
+        return result
+    }
+}
diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/signatures/KtFe10VariableLikeSignature.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/signatures/KtFe10VariableLikeSignature.kt
new file mode 100644
index 0000000..4d615c8
--- /dev/null
+++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/signatures/KtFe10VariableLikeSignature.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010-2023 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.descriptors.signatures
+
+import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
+import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
+import org.jetbrains.kotlin.analysis.api.signatures.KtVariableLikeSignature
+import org.jetbrains.kotlin.analysis.api.symbols.KtVariableLikeSymbol
+import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
+import org.jetbrains.kotlin.analysis.api.types.KtType
+
+internal class KtFe10VariableLikeSignature<out S : KtVariableLikeSymbol>(
+    private val _symbol: S,
+    private val _returnType: KtType,
+    private val _receiverType: KtType?,
+) : KtVariableLikeSignature<S>() {
+    override val token: KtLifetimeToken
+        get() = _symbol.token
+    override val symbol: S
+        get() = withValidityAssertion { _symbol }
+    override val returnType: KtType
+        get() = withValidityAssertion { _returnType }
+    override val receiverType: KtType?
+        get() = withValidityAssertion { _receiverType }
+
+    override fun substitute(substitutor: KtSubstitutor): KtVariableLikeSignature<S> = withValidityAssertion {
+        KtFe10VariableLikeSignature(
+            symbol,
+            substitutor.substitute(returnType),
+            receiverType?.let { substitutor.substitute(it) },
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as KtVariableLikeSignature<*>
+
+        if (symbol != other.symbol) return false
+        if (returnType != other.returnType) return false
+        if (receiverType != other.receiverType) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = symbol.hashCode()
+        result = 31 * result + returnType.hashCode()
+        result = 31 * result + (receiverType?.hashCode() ?: 0)
+        return result
+    }
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt
index 572c4c9..ea26b2d 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt
@@ -62,6 +62,8 @@
 import org.jetbrains.kotlin.types.Variance
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
+import org.jetbrains.kotlin.analysis.api.fir.signatures.KtFirFunctionLikeSubstitutorBasedSignature
+import org.jetbrains.kotlin.analysis.api.fir.signatures.KtFirVariableLikeSubstitutorBasedSignature
 
 /**
  * Maps FirElement to KtSymbol & ConeType to KtType, thread safe
@@ -219,19 +221,7 @@
 
         fun buildFunctionSignature(firSymbol: FirNamedFunctionSymbol): KtFunctionLikeSignature<KtFirFunctionSymbol> {
             firSymbol.lazyResolveToPhase(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE)
-            val functionSymbol = buildFunctionSymbol(firSymbol)
-            return KtFunctionLikeSignature(
-                functionSymbol,
-                typeBuilder.buildKtType(firSymbol.resolvedReturnType),
-                firSymbol.resolvedReceiverTypeRef?.let { typeBuilder.buildKtType(it) },
-                functionSymbol.valueParameters.zip(firSymbol.fir.valueParameters).map { (ktSymbol, fir) ->
-                    var type = fir.returnTypeRef.coneType
-                    if (fir.isVararg) {
-                        type = type.arrayElementType() ?: type
-                    }
-                    KtVariableLikeSignature(ktSymbol, typeBuilder.buildKtType(type), null)
-                }
-            )
+            return KtFirFunctionLikeSubstitutorBasedSignature(analysisSession.token, firSymbol, analysisSession.firSymbolBuilder)
         }
 
         fun buildAnonymousFunctionSymbol(firSymbol: FirAnonymousFunctionSymbol): KtFirAnonymousFunctionSymbol {
@@ -311,11 +301,7 @@
 
         fun buildPropertySignature(firSymbol: FirPropertySymbol): KtVariableLikeSignature<KtVariableSymbol> {
             firSymbol.lazyResolveToPhase(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE)
-            return KtVariableLikeSignature(
-                buildPropertySymbol(firSymbol),
-                typeBuilder.buildKtType(firSymbol.fir.returnTypeRef),
-                firSymbol.resolvedReceiverTypeRef?.let { typeBuilder.buildKtType(it) }
-            )
+            return KtFirVariableLikeSubstitutorBasedSignature(analysisSession.token, firSymbol, analysisSession.firSymbolBuilder)
         }
 
         fun buildLocalVariableSymbol(firSymbol: FirPropertySymbol): KtFirLocalVariableSymbol {
@@ -332,6 +318,9 @@
         }
 
         fun buildValueParameterSymbol(firSymbol: FirValueParameterSymbol): KtValueParameterSymbol {
+            firSymbol.fir.unwrapSubstitutionOverrideIfNeeded()?.let {
+                return buildValueParameterSymbol(it.symbol)
+            }
             return symbolsCache.cache(firSymbol) {
                 KtFirValueParameterSymbol(firSymbol, analysisSession)
             }
@@ -529,8 +518,8 @@
     private inline fun <reified T : FirCallableDeclaration> T.unwrapUseSiteSubstitutionOverride(): T? {
         val originalDeclaration = originalForSubstitutionOverride ?: return null
 
-        val containingClass = getContainingClass(rootSession) ?: return null
-        val originalContainingClass = originalDeclaration.getContainingClass(rootSession) ?: return null
+        val containingClass = getContainingMemberOrSelf().getContainingClass(rootSession) ?: return null
+        val originalContainingClass = originalDeclaration.getContainingMemberOrSelf().getContainingClass(rootSession) ?: return null
 
         // If substitution override does not change the containing class of the FIR declaration,
         // it is a use-site substitution override
@@ -539,6 +528,11 @@
         return originalDeclaration
     }
 
+    private fun FirCallableDeclaration.getContainingMemberOrSelf(): FirCallableDeclaration = when (this) {
+        is FirValueParameter -> containingFunctionSymbol.fir
+        else -> this
+    }
+
     /**
      * We want to unwrap a SUBSTITUTION_OVERRIDE wrapper if it doesn't affect the declaration's signature in any way. If the signature
      * is somehow changed, then we want to keep the wrapper.
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSignatureSubstitutor.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSignatureSubstitutor.kt
index 3615ea4..8584e04 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSignatureSubstitutor.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSignatureSubstitutor.kt
@@ -6,8 +6,27 @@
 package org.jetbrains.kotlin.analysis.api.fir.components
 
 import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
-import org.jetbrains.kotlin.analysis.api.impl.base.components.AbstractKtSignatureSubstitutorImpl
+import org.jetbrains.kotlin.analysis.api.fir.signatures.KtFirFunctionLikeDummySignature
+import org.jetbrains.kotlin.analysis.api.fir.signatures.KtFirVariableLikeDummySignature
+import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirSymbol
+import org.jetbrains.kotlin.analysis.api.impl.base.components.AbstractKtSignatureSubstitutor
+import org.jetbrains.kotlin.analysis.api.signatures.KtFunctionLikeSignature
+import org.jetbrains.kotlin.analysis.api.signatures.KtVariableLikeSignature
+import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.KtVariableLikeSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
 
 internal class KtFirSignatureSubstitutor(
     override val analysisSession: KtFirAnalysisSession
-) : AbstractKtSignatureSubstitutorImpl(), KtFirAnalysisSessionComponent
\ No newline at end of file
+) : AbstractKtSignatureSubstitutor(), KtFirAnalysisSessionComponent {
+    override fun <S : KtFunctionLikeSymbol> asSignature(symbol: S): KtFunctionLikeSignature<S> {
+        val firSymbol = (symbol as KtFirSymbol<*>).firSymbol as FirFunctionSymbol<*>
+        return KtFirFunctionLikeDummySignature<S>(analysisSession.token, firSymbol, analysisSession.firSymbolBuilder)
+    }
+
+    override fun <S : KtVariableLikeSymbol> asSignature(symbol: S): KtVariableLikeSignature<S> {
+        val firSymbol = (symbol as KtFirSymbol<*>).firSymbol as FirVariableSymbol<*>
+        return KtFirVariableLikeDummySignature<S>(analysisSession.token, firSymbol, analysisSession.firSymbolBuilder)
+    }
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/FirSymbolBasedSignature.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/FirSymbolBasedSignature.kt
new file mode 100644
index 0000000..96ecfc6
--- /dev/null
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/FirSymbolBasedSignature.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2010-2023 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.signatures
+
+import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
+import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
+
+internal interface FirSymbolBasedSignature {
+    val firSymbol: FirCallableSymbol<*>
+    val firSymbolBuilder: KtSymbolByFirBuilder
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/KtFirFunctionLikeSignature.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/KtFirFunctionLikeSignature.kt
new file mode 100644
index 0000000..097e0ed
--- /dev/null
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/KtFirFunctionLikeSignature.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010-2023 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.signatures
+
+import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
+import org.jetbrains.kotlin.analysis.api.fir.buildSymbol
+import org.jetbrains.kotlin.analysis.api.fir.types.AbstractKtFirSubstitutor
+import org.jetbrains.kotlin.analysis.api.fir.utils.cached
+import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
+import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
+import org.jetbrains.kotlin.analysis.api.signatures.KtFunctionLikeSignature
+import org.jetbrains.kotlin.analysis.api.signatures.KtVariableLikeSignature
+import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.receiverType
+import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
+import org.jetbrains.kotlin.analysis.api.types.KtType
+import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
+import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
+import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
+import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
+
+internal sealed class KtFirFunctionLikeSignature<out S : KtFunctionLikeSymbol> : KtFunctionLikeSignature<S>(), FirSymbolBasedSignature {
+    abstract override fun substitute(substitutor: KtSubstitutor): KtFirFunctionLikeSignature<S>
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as KtFirFunctionLikeSignature<*>
+        return firSymbol == other.firSymbol
+    }
+
+    override fun hashCode(): Int = firSymbol.hashCode()
+}
+
+internal class KtFirFunctionLikeDummySignature<out S : KtFunctionLikeSymbol>(
+    override val token: KtLifetimeToken,
+    override val firSymbol: FirFunctionSymbol<*>,
+    override val firSymbolBuilder: KtSymbolByFirBuilder,
+) : KtFirFunctionLikeSignature<S>() {
+    @Suppress("UNCHECKED_CAST")
+    override val symbol: S
+        get() = withValidityAssertion { firSymbol.buildSymbol(firSymbolBuilder) as S }
+    override val returnType: KtType
+        get() = withValidityAssertion { symbol.returnType }
+    override val receiverType: KtType?
+        get() = withValidityAssertion { symbol.receiverType }
+    override val valueParameters: List<KtVariableLikeSignature<KtValueParameterSymbol>> by cached {
+        firSymbol.valueParameterSymbols.map { KtFirVariableLikeDummySignature(token, it, firSymbolBuilder) }
+    }
+
+    override fun substitute(substitutor: KtSubstitutor): KtFirFunctionLikeSignature<S> = withValidityAssertion {
+        if (substitutor is KtSubstitutor.Empty) return@withValidityAssertion this
+        require(substitutor is AbstractKtFirSubstitutor<*>)
+
+        KtFirFunctionLikeSubstitutorBasedSignature(token, firSymbol, firSymbolBuilder, substitutor.substitutor)
+    }
+}
+
+internal class KtFirFunctionLikeSubstitutorBasedSignature<out S : KtFunctionLikeSymbol>(
+    override val token: KtLifetimeToken,
+    override val firSymbol: FirFunctionSymbol<*>,
+    override val firSymbolBuilder: KtSymbolByFirBuilder,
+    override val coneSubstitutor: ConeSubstitutor = ConeSubstitutor.Empty,
+) : KtFirFunctionLikeSignature<S>(), SubstitutorBasedSignature {
+    @Suppress("UNCHECKED_CAST")
+    override val symbol: S
+        get() = withValidityAssertion { firSymbol.buildSymbol(firSymbolBuilder) as S }
+    override val returnType: KtType by cached {
+        firSymbolBuilder.typeBuilder.buildKtType(coneSubstitutor.substituteOrSelf(firSymbol.resolvedReturnType))
+    }
+    override val receiverType: KtType? by cached {
+        val receiverTypeRef = when (val fir = firSymbol.fir) {
+            is FirPropertyAccessor -> fir.propertySymbol.resolvedReceiverTypeRef
+            else -> firSymbol.resolvedReceiverTypeRef
+        }
+        receiverTypeRef?.let { firSymbolBuilder.typeBuilder.buildKtType(coneSubstitutor.substituteOrSelf(it.type)) }
+    }
+    override val valueParameters: List<KtVariableLikeSignature<KtValueParameterSymbol>> by cached {
+        firSymbol.fir.valueParameters.map { firValueParameter ->
+            KtFirVariableLikeSubstitutorBasedSignature(token, firValueParameter.symbol, firSymbolBuilder, coneSubstitutor)
+        }
+    }
+
+    override fun substitute(substitutor: KtSubstitutor): KtFirFunctionLikeSignature<S> = withValidityAssertion {
+        if (substitutor is KtSubstitutor.Empty) return@withValidityAssertion this
+        require(substitutor is AbstractKtFirSubstitutor<*>)
+        val chainedSubstitutor = ChainedSubstitutor(coneSubstitutor, substitutor.substitutor)
+
+        KtFirFunctionLikeSubstitutorBasedSignature(token, firSymbol, firSymbolBuilder, chainedSubstitutor)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (!super.equals(other)) return false
+
+        other as KtFirFunctionLikeSubstitutorBasedSignature<*>
+        return coneSubstitutor == other.coneSubstitutor
+    }
+
+    override fun hashCode(): Int = 31 * super.hashCode() + coneSubstitutor.hashCode()
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/KtFirVariableLikeSignature.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/KtFirVariableLikeSignature.kt
new file mode 100644
index 0000000..9d1a225
--- /dev/null
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/KtFirVariableLikeSignature.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010-2023 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.signatures
+
+import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
+import org.jetbrains.kotlin.analysis.api.fir.buildSymbol
+import org.jetbrains.kotlin.analysis.api.fir.types.AbstractKtFirSubstitutor
+import org.jetbrains.kotlin.analysis.api.fir.utils.cached
+import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
+import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
+import org.jetbrains.kotlin.analysis.api.signatures.KtVariableLikeSignature
+import org.jetbrains.kotlin.analysis.api.symbols.KtVariableLikeSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.receiverType
+import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
+import org.jetbrains.kotlin.analysis.api.types.KtType
+import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
+import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
+import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
+import org.jetbrains.kotlin.fir.types.arrayElementType
+import org.jetbrains.kotlin.fir.types.coneType
+import org.jetbrains.kotlin.utils.addToStdlib.applyIf
+
+internal sealed class KtFirVariableLikeSignature<out S : KtVariableLikeSymbol> : KtVariableLikeSignature<S>(), FirSymbolBasedSignature {
+    abstract override fun substitute(substitutor: KtSubstitutor): KtFirVariableLikeSignature<S>
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as KtFirVariableLikeSignature<*>
+        return firSymbol == other.firSymbol
+    }
+
+    override fun hashCode(): Int = firSymbol.hashCode()
+}
+
+internal class KtFirVariableLikeDummySignature<out S : KtVariableLikeSymbol>(
+    override val token: KtLifetimeToken,
+    override val firSymbol: FirVariableSymbol<*>,
+    override val firSymbolBuilder: KtSymbolByFirBuilder,
+) : KtFirVariableLikeSignature<S>() {
+    @Suppress("UNCHECKED_CAST")
+    override val symbol: S
+        get() = withValidityAssertion { firSymbol.buildSymbol(firSymbolBuilder) as S }
+    override val returnType: KtType
+        get() = withValidityAssertion { symbol.returnType }
+    override val receiverType: KtType?
+        get() = withValidityAssertion { symbol.receiverType }
+
+    override fun substitute(substitutor: KtSubstitutor): KtFirVariableLikeSignature<S> = withValidityAssertion {
+        if (substitutor is KtSubstitutor.Empty) return@withValidityAssertion this
+        require(substitutor is AbstractKtFirSubstitutor<*>)
+
+        KtFirVariableLikeSubstitutorBasedSignature(token, firSymbol, firSymbolBuilder, substitutor.substitutor)
+    }
+}
+
+internal class KtFirVariableLikeSubstitutorBasedSignature<out S : KtVariableLikeSymbol>(
+    override val token: KtLifetimeToken,
+    override val firSymbol: FirVariableSymbol<*>,
+    override val firSymbolBuilder: KtSymbolByFirBuilder,
+    override val coneSubstitutor: ConeSubstitutor = ConeSubstitutor.Empty,
+) : KtFirVariableLikeSignature<S>(), SubstitutorBasedSignature {
+    @Suppress("UNCHECKED_CAST")
+    override val symbol: S
+        get() = withValidityAssertion { firSymbol.buildSymbol(firSymbolBuilder) as S }
+    override val returnType: KtType by cached {
+        val isVarargValueParameter = (firSymbol as? FirValueParameterSymbol)?.isVararg == true
+        val coneType = firSymbol.resolvedReturnType.applyIf(isVarargValueParameter) { arrayElementType() ?: this }
+
+        firSymbolBuilder.typeBuilder.buildKtType(coneSubstitutor.substituteOrSelf(coneType))
+    }
+    override val receiverType: KtType? by cached {
+        firSymbol.resolvedReceiverTypeRef?.let { typeRef ->
+            firSymbolBuilder.typeBuilder.buildKtType(coneSubstitutor.substituteOrSelf(typeRef.coneType))
+        }
+    }
+
+    override fun substitute(substitutor: KtSubstitutor): KtFirVariableLikeSignature<S> = withValidityAssertion {
+        if (substitutor is KtSubstitutor.Empty) return@withValidityAssertion this
+        require(substitutor is AbstractKtFirSubstitutor<*>)
+        val chainedSubstitutor = ChainedSubstitutor(coneSubstitutor, substitutor.substitutor)
+
+        KtFirVariableLikeSubstitutorBasedSignature(token, firSymbol, firSymbolBuilder, chainedSubstitutor)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (!super.equals(other)) return false
+
+        other as KtFirVariableLikeSubstitutorBasedSignature<*>
+        return coneSubstitutor == other.coneSubstitutor
+    }
+
+    override fun hashCode(): Int = 31 * super.hashCode() + coneSubstitutor.hashCode()
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/SubstitutorBasedSignature.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/SubstitutorBasedSignature.kt
new file mode 100644
index 0000000..f10c651
--- /dev/null
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/signatures/SubstitutorBasedSignature.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2010-2023 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.signatures
+
+import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
+
+internal interface SubstitutorBasedSignature {
+    val coneSubstitutor: ConeSubstitutor
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/AbstractKtSignatureSubstitutor.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/AbstractKtSignatureSubstitutor.kt
new file mode 100644
index 0000000..265719a
--- /dev/null
+++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/AbstractKtSignatureSubstitutor.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.impl.base.components
+
+import org.jetbrains.kotlin.analysis.api.components.KtSignatureSubstitutor
+import org.jetbrains.kotlin.analysis.api.signatures.KtCallableSignature
+import org.jetbrains.kotlin.analysis.api.signatures.KtFunctionLikeSignature
+import org.jetbrains.kotlin.analysis.api.signatures.KtVariableLikeSignature
+import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.KtVariableLikeSymbol
+import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
+import org.jetbrains.kotlin.analysis.utils.errors.unexpectedElementError
+
+abstract class AbstractKtSignatureSubstitutor : KtSignatureSubstitutor() {
+    override fun <S : KtFunctionLikeSymbol> substitute(symbol: S, substitutor: KtSubstitutor): KtFunctionLikeSignature<S> {
+        if (substitutor is KtSubstitutor.Empty) return asSignature(symbol)
+        return asSignature(symbol).substitute(substitutor)
+    }
+
+    override fun <S : KtVariableLikeSymbol> substitute(symbol: S, substitutor: KtSubstitutor): KtVariableLikeSignature<S> {
+        if (substitutor is KtSubstitutor.Empty) return asSignature(symbol)
+        return asSignature(symbol).substitute(substitutor)
+    }
+
+    override fun <S : KtCallableSymbol> asSignature(symbol: S): KtCallableSignature<S> {
+        return when (symbol) {
+            is KtFunctionLikeSymbol -> asSignature(symbol)
+            is KtVariableLikeSymbol -> asSignature(symbol)
+            else -> unexpectedElementError("symbol", symbol)
+        }
+    }
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/AbstractKtSignatureSubstitutorImpl.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/AbstractKtSignatureSubstitutorImpl.kt
deleted file mode 100644
index 9a9cbeb..0000000
--- a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/AbstractKtSignatureSubstitutorImpl.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.impl.base.components
-
-import org.jetbrains.kotlin.analysis.api.components.KtSignatureSubstitutor
-import org.jetbrains.kotlin.analysis.api.signatures.KtCallableSignature
-import org.jetbrains.kotlin.analysis.api.signatures.KtFunctionLikeSignature
-import org.jetbrains.kotlin.analysis.api.signatures.KtVariableLikeSignature
-import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
-import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
-import org.jetbrains.kotlin.analysis.api.symbols.KtVariableLikeSymbol
-import org.jetbrains.kotlin.analysis.api.symbols.receiverType
-import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
-import org.jetbrains.kotlin.analysis.utils.errors.unexpectedElementError
-
-abstract class AbstractKtSignatureSubstitutorImpl : KtSignatureSubstitutor() {
-
-    override fun <S : KtVariableLikeSymbol> substitute(
-        signature: KtVariableLikeSignature<S>,
-        substitutor: KtSubstitutor
-    ): KtVariableLikeSignature<S> {
-        if (substitutor is KtSubstitutor.Empty) return signature
-        return KtVariableLikeSignature(
-            signature.symbol,
-            substitutor.substitute(signature.returnType),
-            signature.receiverType?.let { substitutor.substitute(it) },
-        )
-    }
-
-    override fun <S : KtFunctionLikeSymbol> substitute(
-        signature: KtFunctionLikeSignature<S>,
-        substitutor: KtSubstitutor
-    ): KtFunctionLikeSignature<S> {
-        if (substitutor is KtSubstitutor.Empty) return signature
-        return KtFunctionLikeSignature(
-            signature.symbol,
-            substitutor.substitute(signature.returnType),
-            signature.receiverType?.let { substitutor.substitute(it) },
-            signature.valueParameters.map { substitute(it, substitutor) }
-        )
-    }
-
-    override fun <S : KtFunctionLikeSymbol> substitute(symbol: S, substitutor: KtSubstitutor): KtFunctionLikeSignature<S> {
-        if (substitutor is KtSubstitutor.Empty) return asSignature(symbol)
-        return KtFunctionLikeSignature(
-            symbol,
-            substitutor.substitute(symbol.returnType),
-            symbol.receiverType?.let { substitutor.substitute(it) },
-            symbol.valueParameters.map { substitute(it, substitutor) }
-        )
-    }
-
-    override fun <S : KtVariableLikeSymbol> substitute(symbol: S, substitutor: KtSubstitutor): KtVariableLikeSignature<S> {
-        if (substitutor is KtSubstitutor.Empty) return asSignature(symbol)
-        return KtVariableLikeSignature(
-            symbol,
-            substitutor.substitute(symbol.returnType),
-            symbol.receiverType?.let { substitutor.substitute(it) },
-        )
-    }
-
-    override fun <S : KtCallableSymbol> asSignature(symbol: S): KtCallableSignature<S> {
-        return when (symbol) {
-            is KtFunctionLikeSymbol -> asSignature(symbol)
-            is KtVariableLikeSymbol -> asSignature(symbol)
-            else -> unexpectedElementError("symbol", symbol)
-        }
-    }
-
-    override fun <S : KtFunctionLikeSymbol> asSignature(symbol: S): KtFunctionLikeSignature<S> {
-        return KtFunctionLikeSignature(symbol, symbol.returnType, symbol.receiverType, symbol.valueParameters.map { asSignature(it) })
-    }
-
-    override fun <S : KtVariableLikeSymbol> asSignature(symbol: S): KtVariableLikeSignature<S> {
-        return KtVariableLikeSignature(symbol, symbol.returnType, symbol.receiverType)
-    }
-}
\ No newline at end of file
diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/testUtils.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/testUtils.kt
index bf40e44..3750aad 100644
--- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/testUtils.kt
+++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/testUtils.kt
@@ -33,7 +33,6 @@
 @OptIn(KtAnalysisApiInternals::class)
 internal fun KtAnalysisSession.stringRepresentation(any: Any): String = with(any) {
     fun KtType.render() = asStringForDebugging().replace('/', '.')
-    fun String.indented() = replace("\n", "\n  ")
     return when (this) {
         is KtFunctionLikeSymbol -> buildString {
             append(
@@ -91,6 +90,7 @@
         is Enum<*> -> name
         is Name -> asString()
         is CallableId -> toString()
+        is KtCallableSignature<*> -> this.stringRepresentation()
         else -> buildString {
             val clazz = this@with::class
             val className = clazz.simpleName!!
@@ -114,6 +114,31 @@
     }
 }
 
+context(KtAnalysisSession)
+private fun KtCallableSignature<*>.stringRepresentation(): String = buildString {
+    when (this@stringRepresentation) {
+        is KtFunctionLikeSignature<*> -> append(KtFunctionLikeSignature::class.simpleName)
+        is KtVariableLikeSignature<*> -> append(KtVariableLikeSignature::class.simpleName)
+    }
+    appendLine(":")
+    val memberProperties = listOfNotNull(
+        KtVariableLikeSignature<*>::name.takeIf { this@stringRepresentation is KtVariableLikeSignature<*> },
+        KtCallableSignature<*>::receiverType,
+        KtCallableSignature<*>::returnType,
+        KtCallableSignature<*>::symbol,
+        KtFunctionLikeSignature<*>::valueParameters.takeIf { this@stringRepresentation is KtFunctionLikeSignature<*> },
+        KtCallableSignature<*>::callableIdIfNonLocal
+    )
+    memberProperties.joinTo(this, separator = "\n  ", prefix = "  ") { property ->
+        @Suppress("UNCHECKED_CAST")
+        val value = (property as KProperty1<Any, *>).get(this@stringRepresentation)
+        val valueAsString = value?.let { stringRepresentation(it).indented() }
+        "${property.name} = $valueAsString"
+    }
+}
+
+private fun String.indented() = replace("\n", "\n  ")
+
 internal fun KtAnalysisSession.prettyPrintSignature(signature: KtCallableSignature<*>): String = prettyPrint {
     when (signature) {
         is KtFunctionLikeSignature -> {
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSignatureSubstitutor.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSignatureSubstitutor.kt
index 5651597..36cca81 100644
--- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSignatureSubstitutor.kt
+++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSignatureSubstitutor.kt
@@ -16,31 +16,6 @@
 import org.jetbrains.kotlin.analysis.utils.errors.unexpectedElementError
 
 public abstract class KtSignatureSubstitutor : KtAnalysisSessionComponent() {
-    @Suppress("UNCHECKED_CAST")
-    public open fun <S : KtCallableSymbol> substitute(
-        signature: KtCallableSignature<S>,
-        substitutor: KtSubstitutor
-    ): KtCallableSignature<S> {
-        return when (signature) {
-            is KtFunctionLikeSignature -> {
-                substitute(signature as KtFunctionLikeSignature<KtFunctionLikeSymbol>, substitutor) as KtCallableSignature<S>
-            }
-            is KtVariableLikeSignature -> {
-                substitute(signature as KtVariableLikeSignature<KtVariableLikeSymbol>, substitutor) as KtCallableSignature<S>
-            }
-        }
-    }
-
-    public abstract fun <S : KtFunctionLikeSymbol> substitute(
-        signature: KtFunctionLikeSignature<S>,
-        substitutor: KtSubstitutor
-    ): KtFunctionLikeSignature<S>
-
-    public abstract fun <S : KtVariableLikeSymbol> substitute(
-        signature: KtVariableLikeSignature<S>,
-        substitutor: KtSubstitutor
-    ): KtVariableLikeSignature<S>
-
     public open fun <S : KtCallableSymbol> substitute(symbol: S, substitutor: KtSubstitutor): KtCallableSignature<S> = when (symbol) {
         is KtFunctionLikeSymbol -> substitute(symbol, substitutor)
         is KtVariableLikeSymbol -> substitute(symbol, substitutor)
@@ -51,42 +26,15 @@
 
     public abstract fun <S : KtVariableLikeSymbol> substitute(symbol: S, substitutor: KtSubstitutor): KtVariableLikeSignature<S>
 
-    public open fun <S : KtCallableSymbol> asSignature(symbol: S): KtCallableSignature<S> =
-        substitute(symbol, KtSubstitutor.Empty(token))
+    public abstract fun <S : KtCallableSymbol> asSignature(symbol: S): KtCallableSignature<S>
 
-    public open fun <S : KtFunctionLikeSymbol> asSignature(symbol: S): KtFunctionLikeSignature<S> =
-        substitute(symbol, KtSubstitutor.Empty(token))
+    public abstract fun <S : KtFunctionLikeSymbol> asSignature(symbol: S): KtFunctionLikeSignature<S>
 
-    public open fun <S : KtVariableLikeSymbol> asSignature(symbol: S): KtVariableLikeSignature<S> =
-        substitute(symbol, KtSubstitutor.Empty(token))
+    public abstract fun <S : KtVariableLikeSymbol> asSignature(symbol: S): KtVariableLikeSignature<S>
 }
 
 public interface KtSignatureSubstitutorMixIn : KtAnalysisSessionMixIn {
     /**
-     * Applies a [substitutor] to the given signature and return a new signature with substituted types.
-     *
-     * @see KtSubstitutor.substitute
-     */
-    public fun <S : KtCallableSymbol> KtCallableSignature<S>.substitute(substitutor: KtSubstitutor): KtCallableSignature<S> =
-        withValidityAssertion { analysisSession.signatureSubstitutor.substitute(this, substitutor) }
-
-    /**
-     * Applies a [substitutor] to the given signature and return a new signature with substituted types.
-     *
-     * @see KtSubstitutor.substitute
-     */
-    public fun <S : KtFunctionLikeSymbol> KtFunctionLikeSignature<S>.substitute(substitutor: KtSubstitutor): KtFunctionLikeSignature<S> =
-        withValidityAssertion { analysisSession.signatureSubstitutor.substitute(this, substitutor) }
-
-    /**
-     * Applies a [substitutor] to the given signature and return a new signature with substituted types.
-     *
-     * @see KtSubstitutor.substitute
-     */
-    public fun <S : KtVariableLikeSymbol> KtVariableLikeSignature<S>.substitute(substitutor: KtSubstitutor): KtVariableLikeSignature<S> =
-        withValidityAssertion { analysisSession.signatureSubstitutor.substitute(this, substitutor) }
-
-    /**
      * Applies a [substitutor] to the given symbol and return a signature with substituted types.
      *
      * @see KtSubstitutor.substitute
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtCallableSignature.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtCallableSignature.kt
index 52f3b34..a5c8b98 100644
--- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtCallableSignature.kt
+++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtCallableSignature.kt
@@ -8,6 +8,7 @@
 import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeOwner
 import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
 import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
+import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
 import org.jetbrains.kotlin.analysis.api.types.KtType
 import org.jetbrains.kotlin.name.CallableId
 
@@ -43,6 +44,13 @@
      */
     public open val callableIdIfNonLocal: CallableId? get() = withValidityAssertion { symbol.callableIdIfNonLocal }
 
+    /**
+     * Applies a [substitutor] to the given signature and return a new signature with substituted types.
+     *
+     * @see KtSubstitutor.substitute
+     */
+    public abstract fun substitute(substitutor: KtSubstitutor): KtCallableSignature<S>
+
     abstract override fun equals(other: Any?): Boolean
     abstract override fun hashCode(): Int
 }
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtFunctionLikeSignature.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtFunctionLikeSignature.kt
index 0576652..83b334a 100644
--- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtFunctionLikeSignature.kt
+++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtFunctionLikeSignature.kt
@@ -5,57 +5,19 @@
 
 package org.jetbrains.kotlin.analysis.api.signatures
 
-
-import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
-import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
 import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
 import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
-import org.jetbrains.kotlin.analysis.api.types.KtType
+import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
 
 /**
  * A signature of a function-like symbol. This includes functions, getters, setters, lambdas, etc.
  */
-public class KtFunctionLikeSignature<out S : KtFunctionLikeSymbol>(
-    private val _symbol: S,
-    private val _returnType: KtType,
-    private val _receiverType: KtType?,
-    private val _valueParameters: List<KtVariableLikeSignature<KtValueParameterSymbol>>,
-) : KtCallableSignature<S>() {
-    override val token: KtLifetimeToken
-        get() = _symbol.token
-    override val symbol: S
-        get() = withValidityAssertion { _symbol }
-    override val returnType: KtType
-        get() = withValidityAssertion { _returnType }
-    override val receiverType: KtType?
-        get() = withValidityAssertion { _receiverType }
-
+public abstract class KtFunctionLikeSignature<out S : KtFunctionLikeSymbol> : KtCallableSignature<S>() {
     /**
      * The use-site-substituted value parameters.
      */
-    public val valueParameters: List<KtVariableLikeSignature<KtValueParameterSymbol>>
-        get() = withValidityAssertion { _valueParameters }
+    public abstract val valueParameters: List<KtVariableLikeSignature<KtValueParameterSymbol>>
 
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (javaClass != other?.javaClass) return false
-
-        other as KtFunctionLikeSignature<*>
-
-        if (_symbol != other._symbol) return false
-        if (_returnType != other._returnType) return false
-        if (_receiverType != other._receiverType) return false
-        if (_valueParameters != other._valueParameters) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = _symbol.hashCode()
-        result = 31 * result + _returnType.hashCode()
-        result = 31 * result + (_receiverType?.hashCode() ?: 0)
-        result = 31 * result + _valueParameters.hashCode()
-        return result
-    }
+    abstract override fun substitute(substitutor: KtSubstitutor): KtFunctionLikeSignature<S>
 }
 
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtVariableLikeSignature.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtVariableLikeSignature.kt
index 1403f7f..64a5868 100644
--- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtVariableLikeSignature.kt
+++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/signatures/KtVariableLikeSignature.kt
@@ -8,9 +8,9 @@
 import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationWithArgumentsInfo
 import org.jetbrains.kotlin.analysis.api.annotations.KtConstantAnnotationValue
 import org.jetbrains.kotlin.analysis.api.annotations.annotationsByClassId
-import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
 import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
 import org.jetbrains.kotlin.analysis.api.symbols.KtVariableLikeSymbol
+import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
 import org.jetbrains.kotlin.analysis.api.types.KtType
 import org.jetbrains.kotlin.builtins.StandardNames
 import org.jetbrains.kotlin.name.Name
@@ -21,20 +21,7 @@
 /**
  * A signature of a variable-like symbol. This includes properties, enum entries local variables, etc.
  */
-public class KtVariableLikeSignature<out S : KtVariableLikeSymbol>(
-    private val _symbol: S,
-    private val _returnType: KtType,
-    private val _receiverType: KtType?,
-) : KtCallableSignature<S>() {
-    override val token: KtLifetimeToken
-        get() = _symbol.token
-    override val symbol: S
-        get() = withValidityAssertion { _symbol }
-    override val returnType: KtType
-        get() = withValidityAssertion { _returnType }
-    override val receiverType: KtType?
-        get() = withValidityAssertion { _receiverType }
-
+public abstract class KtVariableLikeSignature<out S : KtVariableLikeSymbol> : KtCallableSignature<S>() {
     /**
      * A name of the variable with respect to the `@ParameterName` annotation. Can be different from the [KtVariableLikeSymbol.name].
      *
@@ -66,11 +53,13 @@
             //
             //   fun foo(x: (item: Int) -> Unit) { x(1) }
             //   fun bar(x: Function1<@ParameterName("item") Int, Unit>) { x(1) }
-            val nameCanBeDeclaredInAnnotation = _symbol.psi == null
+            val nameCanBeDeclaredInAnnotation = symbol.psi == null
 
-            runIf(nameCanBeDeclaredInAnnotation) { getValueFromParameterNameAnnotation() } ?: _symbol.name
+            runIf(nameCanBeDeclaredInAnnotation) { getValueFromParameterNameAnnotation() } ?: symbol.name
         }
 
+    abstract override fun substitute(substitutor: KtSubstitutor): KtVariableLikeSignature<S>
+
     private fun getValueFromParameterNameAnnotation(): Name? {
         val resultingAnnotation = findParameterNameAnnotation() ?: return null
         val parameterNameArgument = resultingAnnotation.arguments
@@ -91,24 +80,4 @@
             implicitAnnotations.singleOrNull()
         }
     }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (javaClass != other?.javaClass) return false
-
-        other as KtVariableLikeSignature<*>
-
-        if (_symbol != other._symbol) return false
-        if (_returnType != other._returnType) return false
-        if (_receiverType != other._receiverType) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = _symbol.hashCode()
-        result = 31 * result + _returnType.hashCode()
-        result = 31 * result + (_receiverType?.hashCode() ?: 0)
-        return result
-    }
 }