[FIR] Add ability to generate members and nested classifiers for generated classes
This commit is contained in:
committed by
TeamCityServer
parent
270962e176
commit
f3a9d70eb6
+12
-5
@@ -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<out FirExtension> = 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<FirNamedFunctionSymbol> = emptyList()
|
||||
open fun generateProperties(callableId: CallableId, owner: FirClassSymbol<*>?): List<FirPropertySymbol> = emptyList()
|
||||
open fun generateConstructors(callableId: CallableId): List<FirConstructorSymbol> = emptyList()
|
||||
|
||||
// Can be called on IMPORTS stage
|
||||
open fun hasPackage(packageFqName: FqName): Boolean = false
|
||||
|
||||
open fun getCallableNamesForGeneratedClass(classSymbol: FirClassSymbol<*>): Set<Name> = emptySet()
|
||||
open fun getNestedClassifiersNamesForGeneratedClass(classSymbol: FirClassSymbol<*>): Set<Name> = emptySet()
|
||||
|
||||
fun interface Factory : FirExtension.Factory<FirDeclarationGenerationExtension>
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -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<FirNamedFunctionSymbol> {
|
||||
@@ -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<FirCallableSymbol<*>>, 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<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
|
||||
destination += functionCache.getValue(CallableId(packageFqName, name), context = null)
|
||||
destination += functionCache.getValue(CallableId(packageFqName, name))
|
||||
}
|
||||
|
||||
@FirSymbolProviderInternals
|
||||
override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) {
|
||||
destination += propertyCache.getValue(CallableId(packageFqName, name), context = null)
|
||||
destination += propertyCache.getValue(CallableId(packageFqName, name))
|
||||
}
|
||||
|
||||
override fun getPackage(fqName: FqName): FqName? {
|
||||
|
||||
+4
-2
@@ -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<Name>? = null,
|
||||
symbolProvider: FirSymbolProvider? = null
|
||||
) : FirScope(), FirContainingNamesAwareScope {
|
||||
) : FirClassDeclaredMemberScope() {
|
||||
private val nestedClassifierScope: FirScope? = if (useLazyNestedClassifierScope) {
|
||||
lazyNestedClassifierScope(klass.symbol.classId, existingNames!!, symbolProvider!!)
|
||||
} else {
|
||||
|
||||
+10
-2
@@ -50,7 +50,11 @@ class FirDeclaredMemberScopeProvider(val useSiteSession: FirSession) : FirSessio
|
||||
existingNames: List<Name>?,
|
||||
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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+154
@@ -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<Name, List<FirNamedFunctionSymbol>, Nothing?> = firCachesFactory.createCache { callableId, _ ->
|
||||
generateMemberFunctions(callableId)
|
||||
}
|
||||
|
||||
private val propertyCache: FirCache<Name, List<FirPropertySymbol>, Nothing?> = firCachesFactory.createCache { callableId, _ ->
|
||||
generateMemberProperties(callableId)
|
||||
}
|
||||
|
||||
private val constructorCache: FirLazyValue<List<FirConstructorSymbol>, Nothing?> = firCachesFactory.createLazyValue {
|
||||
generateConstructors()
|
||||
}
|
||||
|
||||
private val callableNamesCache: FirLazyValue<Set<Name>, Nothing?> = firCachesFactory.createLazyValue {
|
||||
extension.getCallableNamesForGeneratedClass(firClass.symbol)
|
||||
}
|
||||
|
||||
// ------------------------------------------ generators ------------------------------------------
|
||||
|
||||
private fun generateMemberFunctions(name: Name): List<FirNamedFunctionSymbol> {
|
||||
return extension.generateFunctions(CallableId(firClass.classId, name), firClass.symbol)
|
||||
}
|
||||
|
||||
private fun generateMemberProperties(name: Name): List<FirPropertySymbol> {
|
||||
return extension.generateProperties(CallableId(firClass.classId, name), firClass.symbol)
|
||||
}
|
||||
|
||||
private fun generateConstructors(): List<FirConstructorSymbol> {
|
||||
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<Name> {
|
||||
return callableNamesCache.getValue()
|
||||
}
|
||||
|
||||
override fun getClassifierNames(): Set<Name> {
|
||||
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<Name, FirRegularClassSymbol?, Nothing?> =
|
||||
useSiteSession.firCachesFactory.createCache { name, _ ->
|
||||
generateNestedClassifier(name)
|
||||
}
|
||||
|
||||
private val nestedClassifiersNames: FirLazyValue<Set<Name>, 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<Name> {
|
||||
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")
|
||||
}
|
||||
+16
@@ -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
|
||||
|
||||
+24
-14
@@ -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<Name> = emptySet()
|
||||
}
|
||||
|
||||
class FirNestedClassifierScopeImpl(klass: FirClass, useSiteSession: FirSession) : FirNestedClassifierScope(klass, useSiteSession) {
|
||||
private val classIndex: Map<Name, FirRegularClassSymbol> = run {
|
||||
val result = mutableMapOf<Name, FirRegularClassSymbol>()
|
||||
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<Name> = classIndex.keys
|
||||
override fun isEmpty(): Boolean = classIndex.isEmpty()
|
||||
|
||||
override fun getCallableNames(): Set<Name> = emptySet()
|
||||
override fun getClassifierNames(): Set<Name> = classIndex.keys
|
||||
}
|
||||
|
||||
fun FirTypeParameterRef.toConeType(): ConeKotlinType = symbol.toConeType()
|
||||
|
||||
@@ -17,3 +17,14 @@ inline fun <K : Any, V> FirCache<K, V, Nothing?>.getValue(key: K): V =
|
||||
operator fun <K : Any, V> FirCache<K, V, Nothing>.contains(key: K): Boolean {
|
||||
return getValueIfComputed(key) != null
|
||||
}
|
||||
|
||||
class FirLazyValue<out V, in CONTEXT>(private val cache: FirCache<Unit, V, CONTEXT>) {
|
||||
fun getValue(context: CONTEXT): V {
|
||||
return cache.getValue(Unit, context)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <V> FirLazyValue<V, Nothing?>.getValue(): V {
|
||||
return getValue(null)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,10 @@ abstract class FirCachesFactory : FirSessionComponent {
|
||||
createValue: (K, CONTEXT) -> Pair<V, DATA>,
|
||||
postCompute: (K, V, DATA) -> Unit
|
||||
): FirCache<K, V, CONTEXT>
|
||||
|
||||
fun <V, CONTEXT> createLazyValue(createValue: (CONTEXT) -> V): FirLazyValue<V, CONTEXT> {
|
||||
return FirLazyValue(createCache { _, context -> createValue(context) })
|
||||
}
|
||||
}
|
||||
|
||||
val FirSession.firCachesFactory: FirCachesFactory by FirSession.sessionComponentAccessor()
|
||||
|
||||
@@ -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!!
|
||||
|
||||
@@ -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();
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ class FirAllOpenComponentRegistrar : FirExtensionRegistrar() {
|
||||
// +::AllOpenNestedClassGenerator
|
||||
+::AllOpenAdditionalCheckers
|
||||
+::AllOpenTopLevelDeclarationsGenerator
|
||||
+::AllOpenClassGenerator
|
||||
// +::AllOpenRecursiveNestedClassGenerator
|
||||
}
|
||||
}
|
||||
|
||||
+202
@@ -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<FirRegularClassSymbol>()
|
||||
}
|
||||
private val classIdsForMatchedClasses: Map<ClassId, FirRegularClassSymbol> 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<FirConstructorSymbol> {
|
||||
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<FirNamedFunctionSymbol> {
|
||||
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<Name> {
|
||||
return if (classSymbol.classId in classIdsForMatchedClasses) {
|
||||
setOf(MATERIALIZE_NAME)
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNestedClassifiersNamesForGeneratedClass(classSymbol: FirClassSymbol<*>): Set<Name> {
|
||||
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)
|
||||
-27
@@ -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|kotlin/Any|>()
|
||||
}
|
||||
|
||||
@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|kotlin/Any|>()
|
||||
}
|
||||
|
||||
@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|<local>/a|.<Unresolved name: helloX>#()
|
||||
}
|
||||
public final fun test_2(b: R|B|): R|kotlin/Unit| {
|
||||
R|<local>/b|.<Unresolved name: helloX>#()
|
||||
}
|
||||
@@ -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.<!UNRESOLVED_REFERENCE!>helloX<!>() // should be an error
|
||||
}
|
||||
+28
@@ -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<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
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<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
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|<local>/nestedFoo|.R|foo/AllOpenGenerated.NestedFoo.materialize|().R|bar/Foo.foo|()
|
||||
lval nestedBar: R|foo/AllOpenGenerated.NestedBar| = Q|foo/AllOpenGenerated|.R|foo/AllOpenGenerated.NestedBar|()
|
||||
R|<local>/nestedBar|.R|foo/AllOpenGenerated.NestedBar.materialize|().R|bar/Bar.bar|()
|
||||
}
|
||||
Vendored
+27
@@ -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()
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
FILE: nestedClass.kt
|
||||
public final fun <T> 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<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
private final fun test(): R|A.Foo| {
|
||||
^test <Resolution to classifier>#().<None of the following candidates is applicable because of receiver type mismatch: [/also]>#<R|A.Foo|>(<L> = also@fun <anonymous>(it: R|A.Foo|): R|kotlin/Unit| <inline=Unknown> {
|
||||
R|<local>/it|.<Unresolved name: hello>#()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
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<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
private final fun test(): <ERROR TYPE REF: Symbol not found for Foo> {
|
||||
^test <Unresolved name: Foo>#().<None of the following candidates is applicable because of receiver type mismatch: [/also]>#<R|ERROR CLASS: Cannot infer argument for type parameter T|>(<L> = also@fun <anonymous>(it: <ERROR TYPE REF: Cannot infer argument for type parameter T>): R|kotlin/Unit| <inline=Unknown> {
|
||||
R|<local>/it|.<Unresolved name: hello>#()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import org.jetbrains.kotlin.fir.plugin.WithNestedFoo
|
||||
|
||||
fun <T> 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 <!UNRESOLVED_REFERENCE!>Foo<!>().<!INAPPLICABLE_CANDIDATE!>also<!> {
|
||||
<!UNRESOLVED_REFERENCE!>it<!>.<!UNRESOLVED_REFERENCE!>hello<!>() // should be an error
|
||||
}
|
||||
}
|
||||
}
|
||||
-19
@@ -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|kotlin/Any|>()
|
||||
}
|
||||
|
||||
@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: <ERROR TYPE REF: Symbol not found for SomeClass.Nested.Nested.Nested>): R|kotlin/Unit| {
|
||||
}
|
||||
@@ -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) {}
|
||||
+3
-15
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user