[Analysis API] KTIJ-24527 Properly handle typealiased functional types

Use expanded ConeTypes to get correct parameters and return types

Also, fix the order of rendering modifiers in `KtFunctionalTypeRenderer`

^KTIJ-24527 Fixed
This commit is contained in:
Roman Golyshev
2023-02-08 21:41:28 +01:00
committed by teamcity
parent b931868d41
commit ac8d5a0ea8
9 changed files with 105 additions and 31 deletions
@@ -124,6 +124,12 @@ public class Fe10IdeNormalAnalysisSourceModuleRendererTestGenerated extends Abst
runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/functionTypes.kt");
}
@Test
@TestMetadata("functionalTypeAliases.kt")
public void testFunctionalTypeAliases() throws Exception {
runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/functionalTypeAliases.kt");
}
@Test
@TestMetadata("genericFunctions.kt")
public void testGenericFunctions() throws Exception {
@@ -55,46 +55,36 @@ internal class KtFirFunctionalType(
override val isReflectType: Boolean
get() = withValidityAssertion { coneType.functionTypeKind(builder.rootSession)?.isReflectType == true }
override val arity: Int
get() = withValidityAssertion {
if (coneType.isExtensionFunctionType) coneType.typeArguments.size - 2
else coneType.typeArguments.size - 1
}
override val arity: Int get() = withValidityAssertion { parameterTypes.size }
@OptIn(KtAnalysisApiInternals::class)
override val contextReceivers: List<KtContextReceiver> by cached {
ownTypeArguments.subList(0, coneType.contextReceiversNumberForFunctionType).map { typeProjection ->
// Context receivers in function types may not have labels, hence the `null` label.
KtContextReceiverImpl((typeProjection as KtTypeArgumentWithVariance).type, _label = null, token)
}
coneType.contextReceiversTypes(builder.rootSession)
.map {
// Context receivers in function types may not have labels, hence the `null` label.
KtContextReceiverImpl(it.buildKtType(), _label = null, token)
}
}
override val hasContextReceivers: Boolean get() = withValidityAssertion { coneType.hasContextReceivers }
override val hasContextReceivers: Boolean get() = withValidityAssertion { contextReceivers.isNotEmpty() }
override val receiverType: KtType?
get() = withValidityAssertion {
if (coneType.isExtensionFunctionType) {
// The extension receiver type follows any context receiver types.
(ownTypeArguments[coneType.contextReceiversNumberForFunctionType] as KtTypeArgumentWithVariance).type
} else null
}
override val receiverType: KtType? by cached {
coneType.receiverType(builder.rootSession)?.buildKtType()
}
override val hasReceiver: Boolean
get() = withValidityAssertion {
coneType.receiverType(builder.rootSession) != null
}
override val hasReceiver: Boolean get() = withValidityAssertion { receiverType != null }
override val parameterTypes: List<KtType> by cached {
// Parameter types follow context and extension receiver types.
val firstIndex = coneType.contextReceiversNumberForFunctionType + (if (coneType.isExtensionFunctionType) 1 else 0)
val parameterTypeArgs = ownTypeArguments.subList(firstIndex, ownTypeArguments.lastIndex)
parameterTypeArgs.map { (it as KtTypeArgumentWithVariance).type }
coneType.valueParameterTypesWithoutReceivers(builder.rootSession).map { it.buildKtType() }
}
override val returnType: KtType
get() = withValidityAssertion { (ownTypeArguments.last() as KtTypeArgumentWithVariance).type }
override val returnType: KtType by cached {
coneType.returnType(builder.rootSession).buildKtType()
}
override fun asStringForDebugging(): String = withValidityAssertion { coneType.renderForDebugging() }
override fun equals(other: Any?) = typeEquals(other)
override fun hashCode() = typeHashcode()
private fun ConeKotlinType.buildKtType(): KtType = builder.typeBuilder.buildKtType(this)
}
@@ -124,6 +124,12 @@ public class FirIdeDependentAnalysisSourceModuleRendererTestGenerated extends Ab
runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/functionTypes.kt");
}
@Test
@TestMetadata("functionalTypeAliases.kt")
public void testFunctionalTypeAliases() throws Exception {
runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/functionalTypeAliases.kt");
}
@Test
@TestMetadata("genericFunctions.kt")
public void testGenericFunctions() throws Exception {
@@ -124,6 +124,12 @@ public class FirIdeNormalAnalysisSourceModuleRendererTestGenerated extends Abstr
runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/functionTypes.kt");
}
@Test
@TestMetadata("functionalTypeAliases.kt")
public void testFunctionalTypeAliases() throws Exception {
runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/functionalTypeAliases.kt");
}
@Test
@TestMetadata("genericFunctions.kt")
public void testGenericFunctions() throws Exception {
@@ -124,6 +124,12 @@ public class FirStandaloneNormalAnalysisSourceModuleRendererTestGenerated extend
runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/functionTypes.kt");
}
@Test
@TestMetadata("functionalTypeAliases.kt")
public void testFunctionalTypeAliases() throws Exception {
runTest("analysis/analysis-api/testData/components/symbolDeclarationRenderer/renderDeclaration/functionalTypeAliases.kt");
}
@Test
@TestMetadata("genericFunctions.kt")
public void testGenericFunctions() throws Exception {
@@ -25,13 +25,13 @@ public interface KtFunctionalTypeRenderer {
if (annotationsRendered || type.nullability == KtTypeNullability.NULLABLE) append("(")
" ".separated(
{
if (type.hasContextReceivers) {
contextReceiversRenderer.renderContextReceivers(type, printer)
if (type.isSuspend) {
keywordRenderer.renderKeyword(KtTokens.SUSPEND_KEYWORD, type, printer)
}
},
{
if (type.isSuspend) {
keywordRenderer.renderKeyword(KtTokens.SUSPEND_KEYWORD, type, printer)
if (type.hasContextReceivers) {
contextReceiversRenderer.renderContextReceivers(type, printer)
}
},
{
@@ -0,0 +1,17 @@
class A
class B
typealias WithGeneric<T> = (T) -> String
fun withGeneric(f: WithGeneric<Double>) {}
typealias WithReceiver = String.() -> Unit
fun withReceiver(f: WithReceiver) {}
typealias WithContextReceiver = context(String) () -> Unit
fun withContextReceiver(f: WithContextReceiver) {}
typealias WithSuspend = suspend (String) -> Int
fun withContextReceiver(f: WithSuspend) {}
typealias WithEverything<T> = suspend context(T, B) String.(Int, T) -> String
fun withEverything(f: WithEverything<A>) {}
@@ -0,0 +1,24 @@
class A
class B
typealias WithGeneric<T> = (T) -> String
fun withGeneric(f: (Double) -> String)
typealias WithReceiver = String.() -> Unit
fun withReceiver(f: String.() -> Unit)
typealias WithContextReceiver = context(String) () -> Unit
fun withContextReceiver(f: context(String) () -> Unit)
typealias WithSuspend = suspend (String) -> Int
fun withContextReceiver(f: suspend (String) -> Int)
typealias WithEverything<T> = suspend context(T, B) String.(Int, T) -> String
fun withEverything(f: suspend context(A, B) String.(Int, A) -> String)
@@ -229,6 +229,14 @@ fun ConeKotlinType.findContributedInvokeSymbol(
// ---------------------------------------------- function type type argument extraction ----------------------------------------------
fun ConeKotlinType.contextReceiversTypes(session: FirSession): List<ConeKotlinType> {
if (!isSomeFunctionType(session)) return emptyList()
return fullyExpandedType(session).let { expanded ->
val contextReceivers = expanded.typeArguments.take(expanded.contextReceiversNumberForFunctionType)
contextReceivers.map { it.typeOrDefault(session.builtinTypes.nothingType.type) }
}
}
fun ConeKotlinType.receiverType(session: FirSession): ConeKotlinType? {
if (!isSomeFunctionType(session) || !isExtensionFunctionType(session)) return null
return fullyExpandedType(session).let { expanded ->
@@ -242,6 +250,17 @@ fun ConeKotlinType.returnType(session: FirSession): ConeKotlinType {
return fullyExpandedType(session).typeArguments.last().typeOrDefault(session.builtinTypes.nullableAnyType.type)
}
fun ConeKotlinType.valueParameterTypesWithoutReceivers(session: FirSession): List<ConeKotlinType> {
require(this is ConeClassLikeType)
// TODO: add requirement
val expandedType = fullyExpandedType(session)
val receiversNumber = expandedType.contextReceiversNumberForFunctionType + if (expandedType.isExtensionFunctionType) 1 else 0
val valueParameters = expandedType.typeArguments.drop(receiversNumber).dropLast(1)
return valueParameters.map { it.typeOrDefault(session.builtinTypes.nothingType.type) }
}
fun ConeKotlinType.valueParameterTypesIncludingReceiver(session: FirSession): List<ConeKotlinType> {
require(this is ConeClassLikeType)
// TODO: add requirement