[FIR] Add ability to generate members and nested classifiers for existing classes

This commit is contained in:
Dmitriy Novozhilov
2021-09-29 15:54:10 +03:00
committed by TeamCityServer
parent 9bfa6c54b8
commit 26fa772846
17 changed files with 359 additions and 113 deletions
@@ -13,13 +13,11 @@ class FirAllOpenComponentRegistrar : FirExtensionRegistrar() {
+::AllOpenStatusTransformer
+::AllOpenVisibilityTransformer
+::AllOpenSupertypeGenerator
+::AllOpenAdditionalCheckers
// Declaration generators
// +::AllOpenMemberGenerator
// +::AllOpenNestedClassGenerator
+::AllOpenAdditionalCheckers
+::AllOpenTopLevelDeclarationsGenerator
+::AllOpenClassGenerator
// +::AllOpenRecursiveNestedClassGenerator
+::AllOpenMembersGenerator
}
}
@@ -88,24 +88,7 @@ class AllOpenClassGenerator(session: FirSession) : FirDeclarationGenerationExten
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)
return listOf(buildConstructor(classId, callableId, isInner = false).symbol)
}
private val CallableId.isGeneratedConstructor: Boolean
@@ -136,25 +119,7 @@ class AllOpenClassGenerator(session: FirSession) : FirDeclarationGenerationExten
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)
return listOf(buildMaterializeFunction(matchedClassSymbol, callableId).symbol)
}
private fun buildClass(classId: ClassId): FirRegularClass {
@@ -170,7 +135,7 @@ class AllOpenClassGenerator(session: FirSession) : FirDeclarationGenerationExten
}
}
override fun getCallableNamesForGeneratedClass(classSymbol: FirClassSymbol<*>): Set<Name> {
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
return if (classSymbol.classId in classIdsForMatchedClasses) {
setOf(MATERIALIZE_NAME)
} else {
@@ -178,7 +143,7 @@ class AllOpenClassGenerator(session: FirSession) : FirDeclarationGenerationExten
}
}
override fun getNestedClassifiersNamesForGeneratedClass(classSymbol: FirClassSymbol<*>): Set<Name> {
override fun getNestedClassifiersNames(classSymbol: FirClassSymbol<*>): Set<Name> {
return if (classSymbol.classId == GENERATED_CLASS_ID) {
return classIdsForMatchedClasses.keys.mapTo(mutableSetOf()) { it.shortClassName }
} else {
@@ -0,0 +1,92 @@
/*
* 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.FirPluginKey
import org.jetbrains.kotlin.fir.declarations.builder.buildRegularClass
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.scopes.kotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
/*
* For each class annotated with @C generates
* - member fun materialize(): ClassName
* - nested class Nested with default constructor
*/
class AllOpenMembersGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {
companion object {
private val MATERIALIZE_NAME = Name.identifier("materialize")
private val NESTED_NAME = Name.identifier("Nested")
}
private val predicateBasedProvider = session.predicateBasedProvider
private val matchedClasses by lazy {
predicateBasedProvider.getSymbolsByPredicate(predicate).map { it.symbol }.filterIsInstance<FirRegularClassSymbol>()
}
override fun generateFunctions(callableId: CallableId, owner: FirClassSymbol<*>?): List<FirNamedFunctionSymbol> {
if (callableId.callableName != MATERIALIZE_NAME) return emptyList()
val classId = callableId.classId ?: return emptyList()
val matchedClassSymbol = matchedClasses.firstOrNull { it.classId == classId } ?: return emptyList()
return listOf(buildMaterializeFunction(matchedClassSymbol, callableId).symbol)
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
if (classId.shortClassName != NESTED_NAME) return null
val parentClassId = classId.parentClassId ?: return null
if (matchedClasses.none { it.classId == parentClassId }) return null
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
}.symbol
}
override fun generateConstructors(callableId: CallableId): List<FirConstructorSymbol> {
val classId = callableId.classId ?: return emptyList()
if (callableId.callableName != NESTED_NAME) return emptyList()
if (matchedClasses.none { it.classId == classId }) return emptyList()
return listOf(buildConstructor(classId.createNestedClassId(NESTED_NAME), callableId, isInner = false).symbol)
}
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
return if (classSymbol in matchedClasses) setOf(MATERIALIZE_NAME) else emptySet()
}
override fun getNestedClassifiersNames(classSymbol: FirClassSymbol<*>): Set<Name> {
return if (classSymbol in matchedClasses) setOf(NESTED_NAME) else emptySet()
}
object Key : FirPluginKey() {
override fun toString(): String {
return "AllOpenMembersGeneratorKey"
}
}
override val key: FirPluginKey
get() = Key
override val predicate: DeclarationPredicate
get() = has("C".fqn())
}
@@ -0,0 +1,84 @@
/*
* 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.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildPrimaryConstructor
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.moduleData
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
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
fun FirDeclarationGenerationExtension.buildMaterializeFunction(
matchedClassSymbol: FirClassLikeSymbol<*>,
callableId: CallableId
): FirSimpleFunction {
return 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 = callableId.callableName
symbol = FirNamedFunctionSymbol(callableId)
}
}
@OptIn(SymbolInternals::class)
fun FirDeclarationGenerationExtension.buildConstructor(classId: ClassId, callableId: CallableId, isInner: Boolean): FirConstructor {
val lookupTag = ConeClassLikeLookupTagImpl(classId)
return buildPrimaryConstructor {
moduleData = session.moduleData
origin = key.origin
returnTypeRef = buildResolvedTypeRef {
type = ConeClassLikeTypeImpl(
lookupTag,
emptyArray(),
isNullable = false
)
}
status = FirResolvedDeclarationStatusImpl(
Visibilities.Public,
Modality.FINAL,
EffectiveVisibility.Public
)
symbol = FirConstructorSymbol(callableId)
if (isInner) {
dispatchReceiverType = callableId.classId?.let {
val firClass = session.symbolProvider.getClassLikeSymbolByClassId(it)?.fir as? FirClass
firClass?.defaultType()
}
}
}.also {
it.containingClassForStaticMemberAttr = lookupTag
}
}
@@ -0,0 +1,28 @@
FILE: classWithGeneratedMembersAndNestedClass.kt
@R|org/jetbrains/kotlin/fir/plugin/C|() public final class Foo : R|kotlin/Any| {
public constructor(): R|Foo| {
super<R|kotlin/Any|>()
}
public final class MyNested : R|kotlin/Any| {
public constructor(): R|Foo.MyNested| {
super<R|kotlin/Any|>()
}
}
}
public final class Bar : R|kotlin/Any| {
public constructor(): R|Bar| {
super<R|kotlin/Any|>()
}
}
public final fun test_1(foo: R|Foo|): R|kotlin/Unit| {
lval foo2: R|Foo| = R|<local>/foo|.R|/Foo.materialize|()
lval nested: R|Foo.Nested| = Q|Foo|.R|/Foo.Nested|()
}
public final fun test_2(bar: R|Bar|): R|kotlin/Unit| {
lval foo2: R|Bar| = R|<local>/bar|.<Unresolved name: materialize>#()
lval nested: <ERROR TYPE REF: Unresolved name: Nested> = Q|Bar|.<Unresolved name: Nested>#()
}
@@ -0,0 +1,19 @@
import org.jetbrains.kotlin.fir.plugin.C
@C
class Foo {
class MyNested
}
class Bar
fun test_1(foo: Foo) {
val foo2: Foo = foo.materialize()
val nested = Foo.Nested()
}
// should be errors
fun test_2(bar: Bar) {
val foo2: Bar = bar.<!UNRESOLVED_REFERENCE!>materialize<!>()
val nested = Bar.<!UNRESOLVED_REFERENCE!>Nested<!>()
}
@@ -1,14 +0,0 @@
FILE: topLevelClass.kt
@R|org/jetbrains/kotlin/fir/plugin/A|() public final class SomeClass : R|kotlin/Any| {
public constructor(): R|SomeClass| {
super<R|kotlin/Any|>()
}
}
public final fun test(): R|kotlin/Unit| {
Q|TopLevelSomeClass|.<Unresolved name: hello>#()
}
public final object TopLevelSomeClass {
public final fun hello(): R|kotlin/Int|
}
@@ -1,8 +0,0 @@
import org.jetbrains.kotlin.fir.plugin.A
@A
class SomeClass
fun test() {
TopLevelSomeClass.hello()
}
@@ -49,6 +49,12 @@ public class FirAllOpenDiagnosticTestGenerated extends AbstractFirAllOpenDiagnos
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/fir/fir-plugin-prototype/testData/memberGen"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("classWithGeneratedMembersAndNestedClass.kt")
public void testClassWithGeneratedMembersAndNestedClass() throws Exception {
runTest("plugins/fir/fir-plugin-prototype/testData/memberGen/classWithGeneratedMembersAndNestedClass.kt");
}
@Test
@TestMetadata("generatedClassWithMembersAndNestedClasses.kt")
public void testGeneratedClassWithMembersAndNestedClasses() throws Exception {
@@ -60,12 +66,6 @@ public class FirAllOpenDiagnosticTestGenerated extends AbstractFirAllOpenDiagnos
public void testTopLevelCallables() throws Exception {
runTest("plugins/fir/fir-plugin-prototype/testData/memberGen/topLevelCallables.kt");
}
@Test
@TestMetadata("topLevelClass.kt")
public void testTopLevelClass() throws Exception {
runTest("plugins/fir/fir-plugin-prototype/testData/memberGen/topLevelClass.kt");
}
}
@Nested