diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt index 44a083edb80..710302a180b 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt @@ -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 } diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtTypeCreator.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtTypeCreator.kt new file mode 100644 index 00000000000..f352f5d0ea4 --- /dev/null +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtTypeCreator.kt @@ -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() + + var nullability: KtTypeNullability = KtTypeNullability.NON_NULLABLE + + val arguments: List get() = _arguments + + fun argument(argument: KtType) { + _arguments += argument + } + + class ByClassId(val classId: ClassId) : KtClassTypeBuilder() + class BySymbol(val symbol: KtClassOrObjectSymbol) : KtClassTypeBuilder() +} + diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt index 1f9b53f4562..a4724e822eb 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt @@ -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" diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirAnalysisSessionComponent.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirAnalysisSessionComponent.kt index c2159de12e1..b1f307c29d6 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirAnalysisSessionComponent.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirAnalysisSessionComponent.kt @@ -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, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeCreator.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeCreator.kt new file mode 100644 index 00000000000..b60e76b97f5 --- /dev/null +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeCreator.kt @@ -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 + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/types/FirKtType.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/types/FirKtType.kt index 628d84bd5e9..b8feb8513dc 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/types/FirKtType.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/types/FirKtType.kt @@ -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 by cached { coneType.intersectedTypes.map { conjunct -> firBuilder.typeBuilder.buildKtType(conjunct) } } + + override fun asStringForDebugging(): String = withValidityAssertion { coneType.render() } } internal class KtFirTypeArgumentWithVariance( diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/components/TypeCreatorTest.kt b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/components/TypeCreatorTest.kt new file mode 100644 index 00000000000..8c3fba1940c --- /dev/null +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/components/TypeCreatorTest.kt @@ -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") { + buildClassType(StandardClassIds.List) { + argument(buildClassType(StandardClassIds.Int) { nullability = KtTypeNullability.NULLABLE }) + } + } + } + + @Test + fun testClassTypeBySymbolClassId() { + doTest("MutableList") { + val listSymbol = StandardClassIds.MutableList.getCorrespondingToplevelClassOrObjectSymbol()!! + buildClassType(listSymbol) { + argument(buildClassType(StandardClassIds.Int)) + } + } + } + + @Test + fun testClassTypeByUnresolvedClassId() { + doTest("ERROR_TYPE ") { + buildClassType(ClassId.fromString("NonExistingListClass")) { + argument(buildClassType(StandardClassIds.Int) { nullability = KtTypeNullability.NULLABLE }) + } + } + } + + + companion object { + private val RENDERING_OPTIONS = KtTypeRendererOptions.SHORT_NAMES + } +} \ No newline at end of file