diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirDeclarationGenerationExtension.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirDeclarationGenerationExtension.kt index 34b851d7b4a..cc3edc08915 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirDeclarationGenerationExtension.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirDeclarationGenerationExtension.kt @@ -6,13 +6,11 @@ package org.jetbrains.kotlin.fir.extensions import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import kotlin.reflect.KClass /* @@ -29,11 +27,20 @@ abstract class FirDeclarationGenerationExtension(session: FirSession) : FirPredi final override val extensionType: KClass = FirDeclarationGenerationExtension::class - open fun generateClassLikeDeclaration(classId: ClassId, owner: FirClassSymbol<*>?): FirClassLikeSymbol<*>? = null + // Can be called on SUPERTYPES stage + open fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? = null + + // Can be called on STATUS stage open fun generateFunctions(callableId: CallableId, owner: FirClassSymbol<*>?): List = emptyList() open fun generateProperties(callableId: CallableId, owner: FirClassSymbol<*>?): List = emptyList() + open fun generateConstructors(callableId: CallableId): List = emptyList() + + // Can be called on IMPORTS stage open fun hasPackage(packageFqName: FqName): Boolean = false + open fun getCallableNamesForGeneratedClass(classSymbol: FirClassSymbol<*>): Set = emptySet() + open fun getNestedClassifiersNamesForGeneratedClass(classSymbol: FirClassSymbol<*>): Set = emptySet() + fun interface Factory : FirExtension.Factory } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirExtensionDeclarationsSymbolProvider.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirExtensionDeclarationsSymbolProvider.kt index 0e2ac40a11f..a7008e97e83 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirExtensionDeclarationsSymbolProvider.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirExtensionDeclarationsSymbolProvider.kt @@ -9,13 +9,11 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.caches.FirCache import org.jetbrains.kotlin.fir.caches.FirCachesFactory import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue import org.jetbrains.kotlin.fir.declarations.validate import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals -import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -56,7 +54,7 @@ class FirExtensionDeclarationsSymbolProvider private constructor( private fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? { // TODO: what we should do if multiple extensions want to generate class with same classId? - return extensions.firstNotNullOfOrNull { it.generateClassLikeDeclaration(classId, owner = null) }?.also { it.fir.validate() } + return extensions.firstNotNullOfOrNull { it.generateClassLikeDeclaration(classId) }?.also { it.fir.validate() } } private fun generateTopLevelFunctions(callableId: CallableId): List { @@ -74,23 +72,24 @@ class FirExtensionDeclarationsSymbolProvider private constructor( // ------------------------------------------ provider methods ------------------------------------------ override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { - return classCache.getValue(classId, context = null) + return classCache.getValue(classId) } @FirSymbolProviderInternals override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) { - destination += functionCache.getValue(CallableId(packageFqName, name), context = null) - destination += propertyCache.getValue(CallableId(packageFqName, name), context = null) + val callableId = CallableId(packageFqName, name) + destination += functionCache.getValue(callableId) + destination += propertyCache.getValue(callableId) } @FirSymbolProviderInternals override fun getTopLevelFunctionSymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { - destination += functionCache.getValue(CallableId(packageFqName, name), context = null) + destination += functionCache.getValue(CallableId(packageFqName, name)) } @FirSymbolProviderInternals override fun getTopLevelPropertySymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { - destination += propertyCache.getValue(CallableId(packageFqName, name), context = null) + destination += propertyCache.getValue(CallableId(packageFqName, name)) } override fun getPackage(fqName: FqName): FqName? { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassDeclaredMemberScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassDeclaredMemberScope.kt index 741b5c0fe25..111f1f82dbe 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassDeclaredMemberScope.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassDeclaredMemberScope.kt @@ -17,13 +17,15 @@ import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames -class FirClassDeclaredMemberScope( +abstract class FirClassDeclaredMemberScope : FirScope(), FirContainingNamesAwareScope + +class FirClassDeclaredMemberScopeImpl( val useSiteSession: FirSession, klass: FirClass, useLazyNestedClassifierScope: Boolean = false, existingNames: List? = null, symbolProvider: FirSymbolProvider? = null -) : FirScope(), FirContainingNamesAwareScope { +) : FirClassDeclaredMemberScope() { private val nestedClassifierScope: FirScope? = if (useLazyNestedClassifierScope) { lazyNestedClassifierScope(klass.symbol.classId, existingNames!!, symbolProvider!!) } else { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt index 2eca8deb541..bc4269109ee 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt @@ -50,7 +50,11 @@ class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessio existingNames: List?, symbolProvider: FirSymbolProvider? ): FirClassDeclaredMemberScope { - return FirClassDeclaredMemberScope(useSiteSession, klass, useLazyNestedClassifierScope, existingNames, symbolProvider) + return if (klass.origin.generated) { + FirGeneratedClassDeclaredMemberScope(useSiteSession, klass) + } else { + FirClassDeclaredMemberScopeImpl(useSiteSession, klass, useLazyNestedClassifierScope, existingNames, symbolProvider) + } } fun nestedClassifierScope(klass: FirClass): FirNestedClassifierScope? { @@ -58,7 +62,11 @@ class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessio } private fun createNestedClassifierScope(klass: FirClass): FirNestedClassifierScope? { - return FirNestedClassifierScope(klass, useSiteSession).takeUnless { it.isEmpty() } + return if (klass.origin.generated) { + FirGeneratedClassNestedClassifierScope(klass, useSiteSession) + } else { + FirNestedClassifierScopeImpl(klass, useSiteSession) + }.takeUnless { it.isEmpty() } } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedClassDeclaredMemberScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedClassDeclaredMemberScope.kt new file mode 100644 index 00000000000..4776150394a --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedClassDeclaredMemberScope.kt @@ -0,0 +1,154 @@ +/* + * 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.fir.scopes.impl + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.FirCache +import org.jetbrains.kotlin.fir.caches.FirLazyValue +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.utils.classId +import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension +import org.jetbrains.kotlin.fir.extensions.declarationGenerators +import org.jetbrains.kotlin.fir.extensions.extensionService +import org.jetbrains.kotlin.fir.render +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.symbolProvider +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.Name + +class FirGeneratedClassDeclaredMemberScope( + val useSiteSession: FirSession, + val firClass: FirClass +) : FirClassDeclaredMemberScope() { + private val extension: FirDeclarationGenerationExtension = firClass.findGeneratedExtension(useSiteSession) + private val nestedClassifierScope: FirNestedClassifierScope? = useSiteSession.nestedClassifierScope(firClass) + + private val firCachesFactory = useSiteSession.firCachesFactory + + // ------------------------------------------ caches ------------------------------------------ + + private val functionCache: FirCache, Nothing?> = firCachesFactory.createCache { callableId, _ -> + generateMemberFunctions(callableId) + } + + private val propertyCache: FirCache, Nothing?> = firCachesFactory.createCache { callableId, _ -> + generateMemberProperties(callableId) + } + + private val constructorCache: FirLazyValue, Nothing?> = firCachesFactory.createLazyValue { + generateConstructors() + } + + private val callableNamesCache: FirLazyValue, Nothing?> = firCachesFactory.createLazyValue { + extension.getCallableNamesForGeneratedClass(firClass.symbol) + } + + // ------------------------------------------ generators ------------------------------------------ + + private fun generateMemberFunctions(name: Name): List { + return extension.generateFunctions(CallableId(firClass.classId, name), firClass.symbol) + } + + private fun generateMemberProperties(name: Name): List { + return extension.generateProperties(CallableId(firClass.classId, name), firClass.symbol) + } + + private fun generateConstructors(): List { + val classId = firClass.symbol.classId + val callableId = if (classId.isNestedClass) { + CallableId(classId.parentClassId!!, classId.shortClassName) + } else { + CallableId(classId.asSingleFqName().parent(), classId.shortClassName) + } + return extension.generateConstructors(callableId) + } + + // ------------------------------------------ scope methods ------------------------------------------ + + override fun getCallableNames(): Set { + return callableNamesCache.getValue() + } + + override fun getClassifierNames(): Set { + return nestedClassifierScope?.getClassifierNames() ?: emptySet() + } + + override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) { + nestedClassifierScope?.processClassifiersByNameWithSubstitution(name, processor) + } + + override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) { + if (name !in getCallableNames()) return + for (functionSymbol in functionCache.getValue(name)) { + processor(functionSymbol) + } + } + + override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) { + if (name !in getCallableNames()) return + for (propertySymbol in propertyCache.getValue(name)) { + processor(propertySymbol) + } + } + + override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) { + for (constructorSymbol in constructorCache.getValue()) { + processor(constructorSymbol) + } + } +} + +class FirGeneratedClassNestedClassifierScope( + klass: FirClass, + useSiteSession: FirSession +) : FirNestedClassifierScope(klass, useSiteSession) { + private val extension = klass.findGeneratedExtension(useSiteSession) + + private val nestedClassifierCache: FirCache = + useSiteSession.firCachesFactory.createCache { name, _ -> + generateNestedClassifier(name) + } + + private val nestedClassifiersNames: FirLazyValue, Nothing?> = + useSiteSession.firCachesFactory.createLazyValue { + extension.getNestedClassifiersNamesForGeneratedClass(klass.symbol) + } + + private fun generateNestedClassifier(name: Name): FirRegularClassSymbol? { + if (name !in getClassifierNames()) return null + val generatedClass = useSiteSession.symbolProvider.getClassLikeSymbolByClassId(klass.classId.createNestedClassId(name)) + require(generatedClass is FirRegularClassSymbol?) { "Only regular class are allowed as nested classes" } + return generatedClass + } + + override fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? { + return nestedClassifierCache.getValue(name) + } + + override fun isEmpty(): Boolean { + return getClassifierNames().isEmpty() + } + + override fun getClassifierNames(): Set { + return nestedClassifiersNames.getValue() + } +} + + +private fun FirClass.findGeneratedExtension(useSiteSession: FirSession): FirDeclarationGenerationExtension { + val origin = origin + require(origin is FirDeclarationOrigin.Plugin) { + "GeneratedClassDeclaredMemberScope can not be created for non-generated class: ${this.render()}" + } + + return useSiteSession.extensionService.declarationGenerators.firstOrNull { + it.key == origin.key + } ?: error("Extension for ${origin.key} not found") +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedClassNestedClassifierScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedClassNestedClassifierScope.kt new file mode 100644 index 00000000000..1aa355682a3 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirGeneratedClassNestedClassifierScope.kt @@ -0,0 +1,16 @@ +/* + * 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.fir.scopes.impl + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.FirCache +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.utils.classId +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.name.Name + diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirNestedClassifierScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirNestedClassifierScope.kt index 99030e2fa5a..fd7d1e1cc23 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirNestedClassifierScope.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirNestedClassifierScope.kt @@ -21,7 +21,26 @@ import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl import org.jetbrains.kotlin.name.Name -class FirNestedClassifierScope(val klass: FirClass, val useSiteSession: FirSession) : FirScope(), FirContainingNamesAwareScope { +abstract class FirNestedClassifierScope(val klass: FirClass, val useSiteSession: FirSession) : FirScope(), FirContainingNamesAwareScope { + protected abstract fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? + + override fun processClassifiersByNameWithSubstitution( + name: Name, + processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit + ) { + val matchedClass = getNestedClassSymbol(name) ?: return + val substitution = klass.typeParameters.associate { + it.symbol to it.toConeType() + } + processor(matchedClass, ConeSubstitutorByMap(substitution, useSiteSession)) + } + + abstract fun isEmpty(): Boolean + + override fun getCallableNames(): Set = emptySet() +} + +class FirNestedClassifierScopeImpl(klass: FirClass, useSiteSession: FirSession) : FirNestedClassifierScope(klass, useSiteSession) { private val classIndex: Map = run { val result = mutableMapOf() for (declaration in klass.declarations) { @@ -32,22 +51,13 @@ class FirNestedClassifierScope(val klass: FirClass, val useSiteSession: FirSessi result } - fun isEmpty() = classIndex.isEmpty() - - override fun processClassifiersByNameWithSubstitution( - name: Name, - processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit - ) { - val matchedClass = classIndex[name] ?: return - val substitution = klass.typeParameters.associate { - it.symbol to it.toConeType() - } - processor(matchedClass, ConeSubstitutorByMap(substitution, useSiteSession)) + override fun getNestedClassSymbol(name: Name): FirRegularClassSymbol? { + return classIndex[name] } - override fun getClassifierNames(): Set = classIndex.keys + override fun isEmpty(): Boolean = classIndex.isEmpty() - override fun getCallableNames(): Set = emptySet() + override fun getClassifierNames(): Set = classIndex.keys } fun FirTypeParameterRef.toConeType(): ConeKotlinType = symbol.toConeType() diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCacheWithPostCompute.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCacheWithPostCompute.kt index 18e73cd9043..b593685c004 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCacheWithPostCompute.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCacheWithPostCompute.kt @@ -17,3 +17,14 @@ inline fun FirCache.getValue(key: K): V = operator fun FirCache.contains(key: K): Boolean { return getValueIfComputed(key) != null } + +class FirLazyValue(private val cache: FirCache) { + fun getValue(context: CONTEXT): V { + return cache.getValue(Unit, context) + } +} + +@Suppress("NOTHING_TO_INLINE") +inline fun FirLazyValue.getValue(): V { + return getValue(null) +} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCachesFactory.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCachesFactory.kt index 14bce79dc4c..c247251a05c 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCachesFactory.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCachesFactory.kt @@ -37,6 +37,10 @@ abstract class FirCachesFactory : FirSessionComponent { createValue: (K, CONTEXT) -> Pair, postCompute: (K, V, DATA) -> Unit ): FirCache + + fun createLazyValue(createValue: (CONTEXT) -> V): FirLazyValue { + return FirLazyValue(createCache { _, context -> createValue(context) }) + } } val FirSession.firCachesFactory: FirCachesFactory by FirSession.sessionComponentAccessor() diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt index 24675a1be32..5a251cda55e 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.fir.declarations -sealed class FirDeclarationOrigin(private val displayName: String? = null, val fromSupertypes: Boolean = false) { +sealed class FirDeclarationOrigin(private val displayName: String? = null, val fromSupertypes: Boolean = false, val generated: Boolean = false) { object Source : FirDeclarationOrigin() object Library : FirDeclarationOrigin() object BuiltIns : FirDeclarationOrigin() @@ -18,7 +18,7 @@ sealed class FirDeclarationOrigin(private val displayName: String? = null, val f object IntersectionOverride : FirDeclarationOrigin(fromSupertypes = true) object Delegated : FirDeclarationOrigin() - class Plugin(val key: FirPluginKey) : FirDeclarationOrigin(displayName = "Plugin[$key]") + class Plugin(val key: FirPluginKey) : FirDeclarationOrigin(displayName = "Plugin[$key]", generated = true) override fun toString(): String { return displayName ?: this::class.simpleName!! diff --git a/core/compiler.common/src/org/jetbrains/kotlin/name/ClassId.java b/core/compiler.common/src/org/jetbrains/kotlin/name/ClassId.java index 11f0310c4a2..03fc88fa9ff 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/name/ClassId.java +++ b/core/compiler.common/src/org/jetbrains/kotlin/name/ClassId.java @@ -20,6 +20,8 @@ import kotlin.text.StringsKt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * A class name which is used to uniquely identify a Kotlin class. * @@ -61,6 +63,12 @@ public final class ClassId { return relativeClassName; } + @Nullable + public ClassId getParentClassId() { + if (!isNestedClass()) return null; + return new ClassId(packageFqName, relativeClassName.parent(), isLocal()); + } + @NotNull public Name getShortClassName() { return relativeClassName.shortName(); diff --git a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt index 7ffb5134899..0358b5f7641 100644 --- a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt +++ b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt @@ -19,6 +19,7 @@ class FirAllOpenComponentRegistrar : FirExtensionRegistrar() { // +::AllOpenNestedClassGenerator +::AllOpenAdditionalCheckers +::AllOpenTopLevelDeclarationsGenerator + +::AllOpenClassGenerator // +::AllOpenRecursiveNestedClassGenerator } } diff --git a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/AllOpenClassGenerator.kt b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/AllOpenClassGenerator.kt new file mode 100644 index 00000000000..9d9cc8d20e5 --- /dev/null +++ b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/AllOpenClassGenerator.kt @@ -0,0 +1,202 @@ +/* + * 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.fir.plugin.generators + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.EffectiveVisibility +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.builder.buildPrimaryConstructor +import org.jetbrains.kotlin.fir.declarations.builder.buildRegularClass +import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction +import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl +import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension +import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate +import org.jetbrains.kotlin.fir.extensions.predicate.has +import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.plugin.fqn +import org.jetbrains.kotlin.fir.resolve.symbolProvider +import org.jetbrains.kotlin.fir.scopes.kotlinScopeProvider +import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +/* + * Generates class /foo.AllOpenGenerated with + * - empty public constructor + * - testClassName() functions for all classes annotated with @B + * - NestedClassName nested classes for all classes annotated with @B + * - function `materialize: ClassName` in those nested classes + */ +class AllOpenClassGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) { + companion object { + private val FOO_PACKAGE = FqName.topLevel(Name.identifier("foo")) + private val GENERATED_CLASS_ID = ClassId(FOO_PACKAGE, Name.identifier("AllOpenGenerated")) + private val MATERIALIZE_NAME = Name.identifier("materialize") + } + + object Key : FirPluginKey() { + override fun toString(): String { + return "AllOpenClassGeneratorKey" + } + } + + private val predicateBasedProvider = session.predicateBasedProvider + private val matchedClasses by lazy { + predicateBasedProvider.getSymbolsByPredicate(predicate).map { it.symbol }.filterIsInstance() + } + private val classIdsForMatchedClasses: Map by lazy { + matchedClasses.associateBy { + GENERATED_CLASS_ID.createNestedClassId(Name.identifier("Nested${it.classId.shortClassName}")) + } + } + + override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? { + val owner = classId.outerClassId?.let { session.symbolProvider.getClassLikeSymbolByClassId(it) } as? FirClassSymbol<*> + return when { + owner != null -> when (val origin = owner.origin) { + is FirDeclarationOrigin.Plugin -> when (origin.key) { + Key -> generateNestedClass(classId, owner) + else -> null + } + else -> null + } + else -> generateAllOpenGeneratedClass(classId) + } + } + + private fun generateAllOpenGeneratedClass(classId: ClassId): FirClassLikeSymbol<*>? { + if (classId != GENERATED_CLASS_ID) return null + return buildClass(classId).symbol + } + + override fun generateConstructors(callableId: CallableId): List { + val classId = when { + callableId.isGeneratedConstructor -> GENERATED_CLASS_ID + callableId.isNestedConstructor -> GENERATED_CLASS_ID.createNestedClassId(callableId.callableName) + else -> return emptyList() + } + + val constructor = buildPrimaryConstructor { + moduleData = session.moduleData + origin = key.origin + returnTypeRef = buildResolvedTypeRef { + type = ConeClassLikeTypeImpl( + ConeClassLikeLookupTagImpl(classId), + emptyArray(), + isNullable = false + ) + } + status = FirResolvedDeclarationStatusImpl( + Visibilities.Public, + Modality.FINAL, + EffectiveVisibility.Public + ) + symbol = FirConstructorSymbol(callableId) + } + return listOf(constructor.symbol) + } + + private val CallableId.isGeneratedConstructor: Boolean + get() { + if (classId != null) return false + if (packageName != FOO_PACKAGE) return false + return callableName == GENERATED_CLASS_ID.shortClassName + } + + private val CallableId.isNestedConstructor: Boolean + get() { + if (classId != GENERATED_CLASS_ID) return false + return classIdsForMatchedClasses.keys.any { it.shortClassName == callableName } + } + + private fun generateNestedClass(classId: ClassId, owner: FirClassSymbol<*>): FirClassLikeSymbol<*>? { + if (owner.classId != GENERATED_CLASS_ID) return null + val matchedClass = classIdsForMatchedClasses[classId] ?: return null + + return buildClass(classId).also { + it.matchedClass = matchedClass.classId + }.symbol + } + + @OptIn(SymbolInternals::class) + override fun generateFunctions(callableId: CallableId, owner: FirClassSymbol<*>?): List { + if (callableId.classId !in classIdsForMatchedClasses || callableId.callableName != MATERIALIZE_NAME) return emptyList() + require(owner is FirRegularClassSymbol) + val matchedClassId = owner.fir.matchedClass ?: return emptyList() + val matchedClassSymbol = session.symbolProvider.getClassLikeSymbolByClassId(matchedClassId) ?: return emptyList() + val function = buildSimpleFunction { + moduleData = session.moduleData + origin = key.origin + status = FirResolvedDeclarationStatusImpl( + Visibilities.Public, + Modality.FINAL, + EffectiveVisibility.Public + ) + returnTypeRef = buildResolvedTypeRef { + type = ConeClassLikeTypeImpl( + matchedClassSymbol.toLookupTag(), + emptyArray(), + isNullable = false + ) + } + name = MATERIALIZE_NAME + symbol = FirNamedFunctionSymbol(callableId) + } + return listOf(function.symbol) + } + + private fun buildClass(classId: ClassId): FirRegularClass { + return buildRegularClass { + moduleData = session.moduleData + origin = key.origin + classKind = ClassKind.CLASS + scopeProvider = session.kotlinScopeProvider + status = FirResolvedDeclarationStatusImpl(Visibilities.Public, Modality.FINAL, EffectiveVisibility.Public) + name = classId.shortClassName + symbol = FirRegularClassSymbol(classId) + superTypeRefs += session.builtinTypes.anyType + } + } + + override fun getCallableNamesForGeneratedClass(classSymbol: FirClassSymbol<*>): Set { + return if (classSymbol.classId in classIdsForMatchedClasses) { + setOf(MATERIALIZE_NAME) + } else { + emptySet() + } + } + + override fun getNestedClassifiersNamesForGeneratedClass(classSymbol: FirClassSymbol<*>): Set { + return if (classSymbol.classId == GENERATED_CLASS_ID) { + return classIdsForMatchedClasses.keys.mapTo(mutableSetOf()) { it.shortClassName } + } else { + emptySet() + } + } + + override fun hasPackage(packageFqName: FqName): Boolean { + return packageFqName == FOO_PACKAGE + } + + override val key: FirPluginKey + get() = Key + + override val predicate: DeclarationPredicate + get() = has("B".fqn()) +} + +private object MatchedClassAttributeKey : FirDeclarationDataKey() + +private var FirRegularClass.matchedClass: ClassId? by FirDeclarationDataRegistry.data(MatchedClassAttributeKey) diff --git a/plugins/fir/fir-plugin-prototype/testData/memberGen/functionForProperty.fir.txt b/plugins/fir/fir-plugin-prototype/testData/memberGen/functionForProperty.fir.txt deleted file mode 100644 index 399bfa14970..00000000000 --- a/plugins/fir/fir-plugin-prototype/testData/memberGen/functionForProperty.fir.txt +++ /dev/null @@ -1,27 +0,0 @@ -FILE: functionForProperty.kt - @R|org/jetbrains/kotlin/fir/plugin/WithGenerated|() public final class A : R|kotlin/Any| { - public constructor(): R|A| { - super() - } - - @R|org/jetbrains/kotlin/fir/plugin/WithHello|() public final val x: R|kotlin/Int| = Int(1) - public get(): R|kotlin/Int| - - public final fun helloX(): R|kotlin/Int| - - } - public final class B : R|kotlin/Any| { - public constructor(): R|B| { - super() - } - - @R|org/jetbrains/kotlin/fir/plugin/WithHello|() public final val x: R|kotlin/Int| = Int(1) - public get(): R|kotlin/Int| - - } - public final fun test_1(a: R|A|): R|kotlin/Unit| { - R|/a|.#() - } - public final fun test_2(b: R|B|): R|kotlin/Unit| { - R|/b|.#() - } diff --git a/plugins/fir/fir-plugin-prototype/testData/memberGen/functionForProperty.kt b/plugins/fir/fir-plugin-prototype/testData/memberGen/functionForProperty.kt deleted file mode 100644 index 79e0d1c755d..00000000000 --- a/plugins/fir/fir-plugin-prototype/testData/memberGen/functionForProperty.kt +++ /dev/null @@ -1,22 +0,0 @@ -import org.jetbrains.kotlin.fir.plugin.WithHello -import org.jetbrains.kotlin.fir.plugin.WithGenerated -import org.jetbrains.kotlin.fir.plugin.AllOpen - -@WithGenerated -class A { - @WithHello - val x: Int = 1 -} - -class B { - @WithHello - val x: Int = 1 -} - -fun test_1(a: A) { - a.helloX() // should be OK -} - -fun test_2(b: B) { - b.helloX() // should be an error -} \ No newline at end of file diff --git a/plugins/fir/fir-plugin-prototype/testData/memberGen/generatedClassWithMembersAndNestedClasses.fir.txt b/plugins/fir/fir-plugin-prototype/testData/memberGen/generatedClassWithMembersAndNestedClasses.fir.txt new file mode 100644 index 00000000000..e8c28c0a269 --- /dev/null +++ b/plugins/fir/fir-plugin-prototype/testData/memberGen/generatedClassWithMembersAndNestedClasses.fir.txt @@ -0,0 +1,28 @@ +FILE: generatedClassWithMembersAndNestedClasses.kt + @R|org/jetbrains/kotlin/fir/plugin/B|() public final class Foo : R|kotlin/Any| { + public constructor(): R|bar/Foo| { + super() + } + + public final fun foo(): R|kotlin/Unit| { + } + + } + @R|org/jetbrains/kotlin/fir/plugin/B|() public final class Bar : R|kotlin/Any| { + public constructor(): R|bar/Bar| { + super() + } + + public final fun bar(): R|kotlin/Unit| { + } + + } + public final fun testConstructor(): R|kotlin/Unit| { + lval generatedClass: R|foo/AllOpenGenerated| = R|foo/AllOpenGenerated|() + } + public final fun testNestedClasses(): R|kotlin/Unit| { + lval nestedFoo: R|foo/AllOpenGenerated.NestedFoo| = Q|foo/AllOpenGenerated|.R|foo/AllOpenGenerated.NestedFoo|() + R|/nestedFoo|.R|foo/AllOpenGenerated.NestedFoo.materialize|().R|bar/Foo.foo|() + lval nestedBar: R|foo/AllOpenGenerated.NestedBar| = Q|foo/AllOpenGenerated|.R|foo/AllOpenGenerated.NestedBar|() + R|/nestedBar|.R|foo/AllOpenGenerated.NestedBar.materialize|().R|bar/Bar.bar|() + } diff --git a/plugins/fir/fir-plugin-prototype/testData/memberGen/generatedClassWithMembersAndNestedClasses.kt b/plugins/fir/fir-plugin-prototype/testData/memberGen/generatedClassWithMembersAndNestedClasses.kt new file mode 100644 index 00000000000..f708b5a9ae9 --- /dev/null +++ b/plugins/fir/fir-plugin-prototype/testData/memberGen/generatedClassWithMembersAndNestedClasses.kt @@ -0,0 +1,27 @@ +package bar + +import foo.AllOpenGenerated +import org.jetbrains.kotlin.fir.plugin.B + +@B +class Foo { + fun foo() {} +} + +@B +class Bar { + fun bar() {} +} + +fun testConstructor() { + val generatedClass: AllOpenGenerated = AllOpenGenerated() +} + +fun testNestedClasses() { + val nestedFoo = AllOpenGenerated.NestedFoo() + nestedFoo.materialize().foo() + + val nestedBar = AllOpenGenerated.NestedBar() + nestedBar.materialize().bar() +} + diff --git a/plugins/fir/fir-plugin-prototype/testData/memberGen/nestedClass.fir.txt b/plugins/fir/fir-plugin-prototype/testData/memberGen/nestedClass.fir.txt deleted file mode 100644 index 4f0d74d48b3..00000000000 --- a/plugins/fir/fir-plugin-prototype/testData/memberGen/nestedClass.fir.txt +++ /dev/null @@ -1,37 +0,0 @@ -FILE: nestedClass.kt - public final fun R|T|.also(block: R|(T) -> kotlin/Unit|): R|T| { - ^also this@R|/also| - } - @R|org/jetbrains/kotlin/fir/plugin/WithNestedFoo|() public final class A : R|kotlin/Any| { - public constructor(): R|A| { - super() - } - - private final fun test(): R|A.Foo| { - ^test #().#( = also@fun (it: R|A.Foo|): R|kotlin/Unit| { - R|/it|.#() - } - ) - } - - private final inner class Foo { - public constructor(): R|A.Foo| - - public final fun hello(): R|kotlin/Int| - - } - - } - public final class B : R|kotlin/Any| { - public constructor(): R|B| { - super() - } - - private final fun test(): { - ^test #().#( = also@fun (it: ): R|kotlin/Unit| { - R|/it|.#() - } - ) - } - - } diff --git a/plugins/fir/fir-plugin-prototype/testData/memberGen/nestedClass.kt b/plugins/fir/fir-plugin-prototype/testData/memberGen/nestedClass.kt deleted file mode 100644 index c5f6372f827..00000000000 --- a/plugins/fir/fir-plugin-prototype/testData/memberGen/nestedClass.kt +++ /dev/null @@ -1,20 +0,0 @@ -import org.jetbrains.kotlin.fir.plugin.WithNestedFoo - -fun T.also(block: (T) -> Unit): T = this - -@WithNestedFoo -class A { - private fun test(): Foo { - return Foo().also { - it.hello() // should be OK - } - } -} - -class B { - private fun test(): Foo { - return Foo().also { - it.hello() // should be an error - } - } -} diff --git a/plugins/fir/fir-plugin-prototype/testData/memberGen/recursiveNestedClasses.fir.txt b/plugins/fir/fir-plugin-prototype/testData/memberGen/recursiveNestedClasses.fir.txt deleted file mode 100644 index 1de70fed12e..00000000000 --- a/plugins/fir/fir-plugin-prototype/testData/memberGen/recursiveNestedClasses.fir.txt +++ /dev/null @@ -1,19 +0,0 @@ -FILE: recursiveNestedClasses.kt - @R|org/jetbrains/kotlin/fir/plugin/B|() public final class SomeClass : R|kotlin/Any| { - public constructor(): R|SomeClass| { - super() - } - - @R|org/jetbrains/kotlin/fir/plugin/B|() public final class Nested { - @R|org/jetbrains/kotlin/fir/plugin/B|() public final class Nested { - } - - } - - } - public final fun test_1(x: R|SomeClass.Nested|): R|kotlin/Unit| { - } - public final fun test_2(x: R|SomeClass.Nested.Nested|): R|kotlin/Unit| { - } - public final fun test_3(x: ): R|kotlin/Unit| { - } diff --git a/plugins/fir/fir-plugin-prototype/testData/memberGen/recursiveNestedClasses.kt b/plugins/fir/fir-plugin-prototype/testData/memberGen/recursiveNestedClasses.kt deleted file mode 100644 index c7e6d36a56d..00000000000 --- a/plugins/fir/fir-plugin-prototype/testData/memberGen/recursiveNestedClasses.kt +++ /dev/null @@ -1,8 +0,0 @@ -import org.jetbrains.kotlin.fir.plugin.B - -@B -class SomeClass - -fun test_1(x: SomeClass.Nested) {} -fun test_2(x: SomeClass.Nested.Nested) {} -fun test_3(x: SomeClass.Nested.Nested.Nested) {} \ No newline at end of file diff --git a/plugins/fir/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/FirAllOpenDiagnosticTestGenerated.java b/plugins/fir/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/FirAllOpenDiagnosticTestGenerated.java index 3d3989bed8b..49799d3da72 100644 --- a/plugins/fir/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/FirAllOpenDiagnosticTestGenerated.java +++ b/plugins/fir/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/FirAllOpenDiagnosticTestGenerated.java @@ -50,21 +50,9 @@ public class FirAllOpenDiagnosticTestGenerated extends AbstractFirAllOpenDiagnos } @Test - @TestMetadata("functionForProperty.kt") - public void testFunctionForProperty() throws Exception { - runTest("plugins/fir/fir-plugin-prototype/testData/memberGen/functionForProperty.kt"); - } - - @Test - @TestMetadata("nestedClass.kt") - public void testNestedClass() throws Exception { - runTest("plugins/fir/fir-plugin-prototype/testData/memberGen/nestedClass.kt"); - } - - @Test - @TestMetadata("recursiveNestedClasses.kt") - public void testRecursiveNestedClasses() throws Exception { - runTest("plugins/fir/fir-plugin-prototype/testData/memberGen/recursiveNestedClasses.kt"); + @TestMetadata("generatedClassWithMembersAndNestedClasses.kt") + public void testGeneratedClassWithMembersAndNestedClasses() throws Exception { + runTest("plugins/fir/fir-plugin-prototype/testData/memberGen/generatedClassWithMembersAndNestedClasses.kt"); } @Test