FIR IDE: introduce HL API for creating class types

This commit is contained in:
Ilya Kirillov
2021-06-11 11:42:22 +02:00
committed by TeamCityServer
parent 8094a5448d
commit e2cfd933f1
7 changed files with 208 additions and 18 deletions
@@ -44,8 +44,8 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
KtSymbolDeclarationRendererMixIn,
KtVisibilityCheckerMixIn,
KtMemberSymbolProviderMixin,
KtInheritorsProviderMixIn
{
KtInheritorsProviderMixIn,
KtTypeCreatorMixIn {
override val analysisSession: KtAnalysisSession get() = this
@@ -107,4 +107,7 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
internal val inheritorsProvider: KtInheritorsProvider get() = inheritorsProviderImpl
protected abstract val inheritorsProviderImpl: KtInheritorsProvider
@PublishedApi internal val typesCreator: KtTypeCreator get() = typesCreatorImpl
protected abstract val typesCreatorImpl: KtTypeCreator
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2020 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.idea.frontend.api.components
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.idea.frontend.api.types.KtTypeNullability
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtClassOrObject
abstract class KtTypeCreator : KtAnalysisSessionComponent() {
abstract fun buildClassType(builder: KtClassTypeBuilder): KtClassType
}
interface KtTypeCreatorMixIn : KtAnalysisSessionMixIn
inline fun KtTypeCreatorMixIn.buildClassType(
classId: ClassId,
build: KtClassTypeBuilder.() -> Unit = {}
): KtClassType =
analysisSession.typesCreator.buildClassType(KtClassTypeBuilder.ByClassId(classId).apply(build))
inline fun KtTypeCreatorMixIn.buildClassType(
symbol: KtClassOrObjectSymbol,
build: KtClassTypeBuilder.() -> Unit = {}
): KtClassType =
analysisSession.typesCreator.buildClassType(KtClassTypeBuilder.BySymbol(symbol).apply(build))
sealed class KtTypeBuilder
sealed class KtClassTypeBuilder : KtTypeBuilder() {
private val _arguments = mutableListOf<KtType>()
var nullability: KtTypeNullability = KtTypeNullability.NON_NULLABLE
val arguments: List<KtType> get() = _arguments
fun argument(argument: KtType) {
_arguments += argument
}
class ByClassId(val classId: ClassId) : KtClassTypeBuilder()
class BySymbol(val symbol: KtClassOrObjectSymbol) : KtClassTypeBuilder()
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.components.KtInheritorsProvider
import org.jetbrains.kotlin.idea.frontend.api.components.KtVisibilityChecker
import org.jetbrains.kotlin.idea.frontend.api.components.KtSymbolDeclarationRendererProvider
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeCreator
import org.jetbrains.kotlin.idea.frontend.api.fir.components.*
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirOverrideInfoProvider
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbolProvider
@@ -83,6 +84,8 @@ private constructor(
override val inheritorsProviderImpl: KtInheritorsProvider = KtFirInheritorsProvider(this, token)
override val typesCreatorImpl: KtTypeCreator = KtFirTypeCreator(this, token)
override fun createContextDependentCopy(originalKtFile: KtFile, elementToReanalyze: KtElement): KtAnalysisSession {
check(mode == AnalysisSessionMode.REGULAR) {
"Cannot create context-dependent copy of KtAnalysis session from a context dependent one"
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KT_DIAGNOSTIC_CONVERTER
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
internal interface KtFirAnalysisSessionComponent {
val analysisSession: KtFirAnalysisSession
@@ -42,6 +44,12 @@ internal interface KtFirAnalysisSessionComponent {
return firDiagnostic.asKtDiagnostic()
}
val KtType.coneType: ConeKotlinType
get() {
require(this is KtFirType)
return coneType
}
fun createTypeCheckerContext() = ConeTypeCheckerContext(
isErrorTypeEqualsToAnything = true,
isStubTypeEqualsToAnything = true,
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2021 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.idea.frontend.api.fir.components
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedSymbolError
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.ConeClassErrorType
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.idea.frontend.api.components.KtClassTypeBuilder
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeCreator
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
import org.jetbrains.kotlin.idea.frontend.api.withValidityAssertion
internal class KtFirTypeCreator(
override val analysisSession: KtFirAnalysisSession,
override val token: ValidityToken
) : KtTypeCreator(), KtFirAnalysisSessionComponent {
override fun buildClassType(builder: KtClassTypeBuilder): KtClassType = withValidityAssertion {
val lookupTag = when (builder) {
is KtClassTypeBuilder.ByClassId -> {
val classSymbol = rootModuleSession.symbolProvider.getClassLikeSymbolByFqName(builder.classId)
?: return ConeClassErrorType(ConeUnresolvedSymbolError(builder.classId)).asKtType() as KtClassType
classSymbol.toLookupTag()
}
is KtClassTypeBuilder.BySymbol -> {
val symbol = builder.symbol
check(symbol is KtFirSymbol<*>)
symbol.firRef.withFir { (it as FirClassLikeDeclaration<*>).symbol.toLookupTag() }
}
}
val coneType = rootModuleSession.typeContext.createSimpleType(
lookupTag,
builder.arguments.map { it.coneType },
builder.nullability.isNullable
) as ConeClassLikeType
return coneType.asKtType() as KtClassType
}
}
@@ -20,10 +20,8 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance
internal interface KtFirType : KtType, ValidityTokenOwner {
internal interface KtFirType : ValidityTokenOwner {
val coneType: ConeKotlinType
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
}
internal class KtFirUsualClassType(
@@ -45,10 +43,7 @@ internal class KtFirUsualClassType(
}
override val nullability: KtTypeNullability get() = withValidityAssertion { KtTypeNullability.create(coneType.isNullable) }
override fun asString(): String = withValidityAssertion {
coneType.render() //todo
}
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
}
internal class KtFirFunctionalType(
@@ -93,18 +88,17 @@ internal class KtFirFunctionalType(
override val returnType: KtType
get() = withValidityAssertion { (typeArguments.last() as KtTypeArgumentWithVariance).type }
override fun asString(): String = withValidityAssertion {
coneType.render() //todo
}
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
}
internal class KtFirErrorType(
internal class KtFirClassErrorType(
coneType: ConeClassErrorType,
override val token: ValidityToken,
) : KtErrorType(), KtFirType {
) : KtClassErrorType(), KtFirType {
override val coneType by weakRef(coneType)
override val error: String get() = withValidityAssertion { coneType.diagnostic.reason }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
}
internal class KtFirTypeParameterType(
@@ -121,10 +115,7 @@ internal class KtFirTypeParameterType(
}
override val nullability: KtTypeNullability get() = withValidityAssertion { KtTypeNullability.create(coneType.isNullable) }
override fun asString(): String = withValidityAssertion {
coneType.render() //todo
}
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
}
internal class KtFirFlexibleType(
@@ -136,6 +127,7 @@ internal class KtFirFlexibleType(
override val lowerBound: KtType by cached { firBuilder.typeBuilder.buildKtType(coneType.lowerBound) }
override val upperBound: KtType by cached { firBuilder.typeBuilder.buildKtType(coneType.upperBound) }
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
}
internal class KtFirIntersectionType(
@@ -148,6 +140,8 @@ internal class KtFirIntersectionType(
override val conjuncts: List<KtType> by cached {
coneType.intersectedTypes.map { conjunct -> firBuilder.typeBuilder.buildKtType(conjunct) }
}
override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() }
}
internal class KtFirTypeArgumentWithVariance(
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2021 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.idea.fir.frontend.api.components
import com.intellij.testFramework.LightProjectDescriptor
import org.jetbrains.kotlin.idea.fir.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.idea.fir.invalidateCaches
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.analyse
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
import org.jetbrains.kotlin.idea.frontend.api.components.buildClassType
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.idea.frontend.api.types.KtTypeNullability
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi.KtFile
import org.junit.Test
internal class TypeCreatorTest : KotlinLightCodeInsightFixtureTestCase() {
override fun isFirPlugin() = true
override fun tearDown() {
project.invalidateCaches(file as? KtFile)
super.tearDown()
}
private fun doTest(
expected: String,
buildType: KtAnalysisSession.() -> KtType,
) {
val fakeKtFile = myFixture.configureByText("file.kt", "val a = 10") as KtFile
val renderedType = executeOnPooledThreadInReadAction {
analyse(fakeKtFile) {
val ktType = buildType()
ktType.render(RENDERING_OPTIONS)
}
}
assertEquals(expected, renderedType)
}
override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
@Test
fun testClassTypeByClassId() {
doTest("List<Int?>") {
buildClassType(StandardClassIds.List) {
argument(buildClassType(StandardClassIds.Int) { nullability = KtTypeNullability.NULLABLE })
}
}
}
@Test
fun testClassTypeBySymbolClassId() {
doTest("MutableList<Int>") {
val listSymbol = StandardClassIds.MutableList.getCorrespondingToplevelClassOrObjectSymbol()!!
buildClassType(listSymbol) {
argument(buildClassType(StandardClassIds.Int))
}
}
}
@Test
fun testClassTypeByUnresolvedClassId() {
doTest("ERROR_TYPE <Symbol not found for /NonExistingListClass>") {
buildClassType(ClassId.fromString("NonExistingListClass")) {
argument(buildClassType(StandardClassIds.Int) { nullability = KtTypeNullability.NULLABLE })
}
}
}
companion object {
private val RENDERING_OPTIONS = KtTypeRendererOptions.SHORT_NAMES
}
}