[Lombok] Add implementation of plugin for FIR
This commit is contained in:
committed by
teamcity
parent
e58e86932c
commit
a84ece7233
@@ -0,0 +1,28 @@
|
||||
description = "Lombok compiler plugin (K2)"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":kotlin-lombok-compiler-plugin.common"))
|
||||
compileOnly(project(":compiler:fir:cones"))
|
||||
compileOnly(project(":compiler:fir:tree"))
|
||||
compileOnly(project(":compiler:fir:resolve"))
|
||||
compileOnly(project(":compiler:fir:checkers"))
|
||||
compileOnly(project(":compiler:fir:java"))
|
||||
compileOnly(project(":compiler:fir:entrypoint"))
|
||||
|
||||
compileOnly(intellijCore())
|
||||
runtimeOnly(kotlinStdlib())
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2
|
||||
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokService
|
||||
import org.jetbrains.kotlin.lombok.k2.generators.*
|
||||
import java.io.File
|
||||
|
||||
class FirLombokRegistrar(private val lombokConfigFile: File?) : FirExtensionRegistrar() {
|
||||
override fun ExtensionRegistrarContext.configurePlugin() {
|
||||
+LombokService.getFactory(lombokConfigFile)
|
||||
+::GetterGenerator
|
||||
+::SetterGenerator
|
||||
+::WithGenerator
|
||||
+::LombokConstructorsGenerator
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.config
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent.Factory
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.lombok.config.LombokConfig
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Accessors
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.AllArgsConstructor
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Data
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Getter
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.NoArgsConstructor
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.RequiredArgsConstructor
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Setter
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Value
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.With
|
||||
import java.io.File
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
class LombokService(session: FirSession, configFile: File?) : FirExtensionSessionComponent(session) {
|
||||
companion object {
|
||||
fun getFactory(configFile: File?): Factory {
|
||||
return Factory { LombokService(it, configFile) }
|
||||
}
|
||||
}
|
||||
|
||||
private val config = configFile?.let(LombokConfig::parse) ?: LombokConfig.Empty
|
||||
private val cachesFactory = session.firCachesFactory
|
||||
|
||||
private val accessorsCache: Cache<Accessors> = cachesFactory.createCache { symbol ->
|
||||
Accessors.get(symbol.fir, config)
|
||||
}
|
||||
|
||||
private val accessorsIfAnnotatedCache: Cache<Accessors?> = cachesFactory.createCache { symbol ->
|
||||
Accessors.getIfAnnotated(symbol.fir, config)
|
||||
}
|
||||
|
||||
private val getterCache: Cache<Getter?> = cachesFactory.createCache { symbol ->
|
||||
Getter.getOrNull(symbol.fir)
|
||||
}
|
||||
|
||||
private val setterCache: Cache<Setter?> = cachesFactory.createCache { symbol ->
|
||||
Setter.getOrNull(symbol.fir)
|
||||
}
|
||||
|
||||
private val withCache: Cache<With?> = cachesFactory.createCache { symbol ->
|
||||
With.getOrNull(symbol.fir)
|
||||
}
|
||||
|
||||
private val noArgsConstructorCache: Cache<NoArgsConstructor?> = cachesFactory.createCache { symbol ->
|
||||
NoArgsConstructor.getOrNull(symbol.fir)
|
||||
}
|
||||
|
||||
private val allArgsConstructorCache: Cache<AllArgsConstructor?> = cachesFactory.createCache { symbol ->
|
||||
AllArgsConstructor.getOrNull(symbol.fir)
|
||||
}
|
||||
|
||||
private val requiredArgsConstructorCache: Cache<RequiredArgsConstructor?> = cachesFactory.createCache { symbol ->
|
||||
RequiredArgsConstructor.getOrNull(symbol.fir)
|
||||
}
|
||||
|
||||
private val dataCache: Cache<Data?> = cachesFactory.createCache { symbol ->
|
||||
Data.getOrNull(symbol.fir)
|
||||
}
|
||||
|
||||
private val valueCache: Cache<Value?> = cachesFactory.createCache { symbol ->
|
||||
Value.getOrNull(symbol.fir)
|
||||
}
|
||||
|
||||
fun getAccessors(symbol: FirBasedSymbol<*>): Accessors = accessorsCache.getValue(symbol)
|
||||
fun getAccessorsIfAnnotated(symbol: FirBasedSymbol<*>): Accessors? = accessorsIfAnnotatedCache.getValue(symbol)
|
||||
fun getGetter(symbol: FirBasedSymbol<*>): Getter? = getterCache.getValue(symbol)
|
||||
fun getSetter(symbol: FirBasedSymbol<*>): Setter? = setterCache.getValue(symbol)
|
||||
fun getWith(symbol: FirBasedSymbol<*>): With? = withCache.getValue(symbol)
|
||||
fun getNoArgsConstructor(symbol: FirBasedSymbol<*>): NoArgsConstructor? = noArgsConstructorCache.getValue(symbol)
|
||||
fun getAllArgsConstructor(symbol: FirBasedSymbol<*>): AllArgsConstructor? = allArgsConstructorCache.getValue(symbol)
|
||||
fun getRequiredArgsConstructor(symbol: FirBasedSymbol<*>): RequiredArgsConstructor? = requiredArgsConstructorCache.getValue(symbol)
|
||||
fun getData(symbol: FirBasedSymbol<*>): Data? = dataCache.getValue(symbol)
|
||||
fun getValue(symbol: FirBasedSymbol<*>): Value? = valueCache.getValue(symbol)
|
||||
}
|
||||
|
||||
private typealias Cache<T> = FirCache<FirBasedSymbol<*>, T, Nothing?>
|
||||
|
||||
val FirSession.lombokService: LombokService by FirSession.sessionComponentAccessor()
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.config
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.declarations.getBooleanArgument
|
||||
import org.jetbrains.kotlin.fir.declarations.getStringArrayArgument
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.lombok.config.AccessLevel
|
||||
import org.jetbrains.kotlin.lombok.config.LombokConfig
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.ACCESS
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.CHAIN
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.CHAIN_CONFIG
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.FLUENT
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.FLUENT_CONFIG
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.NO_IS_PREFIX_CONFIG
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.PREFIX
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.PREFIX_CONFIG
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.STATIC_CONSTRUCTOR
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokConfigNames.STATIC_NAME
|
||||
import org.jetbrains.kotlin.lombok.utils.LombokNames
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
/*
|
||||
* Lombok has two ways of configuration - lombok.config file and directly in annotations. Annotations has priority.
|
||||
* Not all things can be configured in annotations
|
||||
* So to make things easier I put all configuration in 'annotations' classes, but populate them from config too. So far it allows
|
||||
* keeping processors' code unaware about configuration origin.
|
||||
*
|
||||
*/
|
||||
|
||||
fun List<FirAnnotation>.findAnnotation(classId: ClassId): FirAnnotation? {
|
||||
return firstOrNull { it.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId == classId }
|
||||
}
|
||||
|
||||
abstract class ConeAnnotationCompanion<T>(val name: ClassId) {
|
||||
|
||||
abstract fun extract(annotation: FirAnnotation): T
|
||||
|
||||
fun getOrNull(annotated: FirAnnotationContainer): T? {
|
||||
return annotated.annotations.findAnnotation(name)?.let(this::extract)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ConeAnnotationAndConfigCompanion<T>(val annotationName: ClassId) {
|
||||
|
||||
abstract fun extract(annotation: FirAnnotation?, config: LombokConfig): T
|
||||
|
||||
/**
|
||||
* Get from annotation or config or default
|
||||
*/
|
||||
fun get(annotated: FirAnnotationContainer, config: LombokConfig): T =
|
||||
extract(annotated.annotations.findAnnotation(annotationName), config)
|
||||
|
||||
/**
|
||||
* If element is annotated, get from it or config or default
|
||||
*/
|
||||
fun getIfAnnotated(annotated: FirAnnotationContainer, config: LombokConfig): T? =
|
||||
annotated.annotations.findAnnotation(annotationName)?.let { annotation ->
|
||||
extract(annotation, config)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object ConeLombokAnnotations {
|
||||
class Accessors(
|
||||
val fluent: Boolean = false,
|
||||
val chain: Boolean = false,
|
||||
val noIsPrefix: Boolean = false,
|
||||
val prefix: List<String> = emptyList()
|
||||
) {
|
||||
companion object : ConeAnnotationAndConfigCompanion<Accessors>(LombokNames.ACCESSORS_ID) {
|
||||
override fun extract(annotation: FirAnnotation?, config: LombokConfig): Accessors {
|
||||
val fluent = annotation?.getBooleanArgument(FLUENT)
|
||||
?: config.getBoolean(FLUENT_CONFIG)
|
||||
?: false
|
||||
val chain = annotation?.getBooleanArgument(CHAIN)
|
||||
?: config.getBoolean(CHAIN_CONFIG)
|
||||
?: fluent
|
||||
val noIsPrefix = config.getBoolean(NO_IS_PREFIX_CONFIG) ?: false
|
||||
val prefix = annotation?.getStringArrayArgument(PREFIX)
|
||||
?: config.getMultiString(PREFIX_CONFIG)
|
||||
?: emptyList()
|
||||
|
||||
return Accessors(fluent, chain, noIsPrefix, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Getter(val visibility: AccessLevel = AccessLevel.PUBLIC) {
|
||||
companion object : ConeAnnotationCompanion<Getter>(LombokNames.GETTER_ID) {
|
||||
override fun extract(annotation: FirAnnotation): Getter = Getter(
|
||||
visibility = getAccessLevel(annotation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Setter(val visibility: AccessLevel = AccessLevel.PUBLIC) {
|
||||
companion object : ConeAnnotationCompanion<Setter>(LombokNames.SETTER_ID) {
|
||||
override fun extract(annotation: FirAnnotation): Setter = Setter(
|
||||
visibility = getAccessLevel(annotation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class With(val visibility: AccessLevel = AccessLevel.PUBLIC) {
|
||||
companion object : ConeAnnotationCompanion<With>(LombokNames.WITH_ID) {
|
||||
override fun extract(annotation: FirAnnotation): With = With(
|
||||
visibility = getAccessLevel(annotation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
interface ConstructorAnnotation {
|
||||
val visibility: Visibility
|
||||
val staticName: String?
|
||||
}
|
||||
|
||||
class NoArgsConstructor(
|
||||
override val visibility: Visibility,
|
||||
override val staticName: String?
|
||||
) : ConstructorAnnotation {
|
||||
companion object : ConeAnnotationCompanion<NoArgsConstructor>(LombokNames.NO_ARGS_CONSTRUCTOR_ID) {
|
||||
override fun extract(annotation: FirAnnotation): NoArgsConstructor = NoArgsConstructor(
|
||||
visibility = getVisibility(annotation, ACCESS),
|
||||
staticName = annotation.getNonBlankStringArgument(STATIC_NAME)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AllArgsConstructor(
|
||||
override val visibility: Visibility = Visibilities.Public,
|
||||
override val staticName: String? = null
|
||||
) : ConstructorAnnotation {
|
||||
companion object : ConeAnnotationCompanion<AllArgsConstructor>(LombokNames.ALL_ARGS_CONSTRUCTOR_ID) {
|
||||
override fun extract(annotation: FirAnnotation): AllArgsConstructor = AllArgsConstructor(
|
||||
visibility = getVisibility(annotation, ACCESS),
|
||||
staticName = annotation.getNonBlankStringArgument(STATIC_NAME)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class RequiredArgsConstructor(
|
||||
override val visibility: Visibility = Visibilities.Public,
|
||||
override val staticName: String? = null
|
||||
) : ConstructorAnnotation {
|
||||
companion object : ConeAnnotationCompanion<RequiredArgsConstructor>(LombokNames.REQUIRED_ARGS_CONSTRUCTOR_ID) {
|
||||
override fun extract(annotation: FirAnnotation): RequiredArgsConstructor = RequiredArgsConstructor(
|
||||
visibility = getVisibility(annotation, ACCESS),
|
||||
staticName = annotation.getNonBlankStringArgument(STATIC_NAME)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Data(val staticConstructor: String?) {
|
||||
fun asSetter(): Setter = Setter()
|
||||
fun asGetter(): Getter = Getter()
|
||||
|
||||
fun asRequiredArgsConstructor(): RequiredArgsConstructor = RequiredArgsConstructor(
|
||||
staticName = staticConstructor
|
||||
)
|
||||
|
||||
companion object : ConeAnnotationCompanion<Data>(LombokNames.DATA_ID) {
|
||||
override fun extract(annotation: FirAnnotation): Data =
|
||||
Data(
|
||||
staticConstructor = annotation.getNonBlankStringArgument(STATIC_CONSTRUCTOR)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Value(val staticConstructor: String?) {
|
||||
fun asGetter(): Getter = Getter()
|
||||
|
||||
fun asAllArgsConstructor(): AllArgsConstructor = AllArgsConstructor(
|
||||
staticName = staticConstructor
|
||||
)
|
||||
|
||||
companion object : ConeAnnotationCompanion<Value>(LombokNames.VALUE_ID) {
|
||||
override fun extract(annotation: FirAnnotation): Value = Value(
|
||||
staticConstructor = annotation.getNonBlankStringArgument(STATIC_CONSTRUCTOR)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.config
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.declarations.findArgumentByName
|
||||
import org.jetbrains.kotlin.fir.declarations.getStringArgument
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirEnumEntrySymbol
|
||||
import org.jetbrains.kotlin.lombok.config.AccessLevel
|
||||
import org.jetbrains.kotlin.lombok.utils.trimToNull
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
fun getAccessLevel(annotation: FirAnnotation, field: Name = LombokConfigNames.VALUE): AccessLevel {
|
||||
val value = annotation.getArgumentAsString(field) ?: return AccessLevel.PUBLIC
|
||||
return AccessLevel.valueOf(value)
|
||||
}
|
||||
|
||||
private fun FirAnnotation.getArgumentAsString(field: Name): String? {
|
||||
val argument = findArgumentByName(field) ?: return null
|
||||
return when (argument) {
|
||||
is FirConstExpression<*> -> argument.value as? String
|
||||
is FirQualifiedAccessExpression -> {
|
||||
val symbol = argument.toResolvedCallableSymbol()
|
||||
if (symbol is FirEnumEntrySymbol) {
|
||||
symbol.callableId.callableName.identifier
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun getVisibility(annotation: FirAnnotation, field: Name = LombokConfigNames.VALUE): Visibility {
|
||||
return getAccessLevel(annotation, field).toVisibility()
|
||||
}
|
||||
|
||||
fun FirAnnotation.getNonBlankStringArgument(name: Name): String? = getStringArgument(name)?.trimToNull()
|
||||
|
||||
object LombokConfigNames {
|
||||
val VALUE = Name.identifier("value")
|
||||
val FLUENT = Name.identifier("fluent")
|
||||
val CHAIN = Name.identifier("chain")
|
||||
val PREFIX = Name.identifier("prefix")
|
||||
val ACCESS = Name.identifier("access")
|
||||
val STATIC_NAME = Name.identifier("staticName")
|
||||
val STATIC_CONSTRUCTOR = Name.identifier("staticConstructor")
|
||||
|
||||
|
||||
const val FLUENT_CONFIG = "lombok.accessors.fluent"
|
||||
const val CHAIN_CONFIG = "lombok.accessors.chain"
|
||||
const val PREFIX_CONFIG = "lombok.accessors.prefix"
|
||||
const val NO_IS_PREFIX_CONFIG = "lombok.getter.noIsPrefix"
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.generators
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildConstructedClassTypeParameterRef
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameterCopy
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isInner
|
||||
import org.jetbrains.kotlin.fir.java.declarations.*
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.toEffectiveVisibility
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.jvm.buildJavaTypeRef
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokService
|
||||
import org.jetbrains.kotlin.lombok.k2.config.lombokService
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.callableIdForConstructor
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
|
||||
abstract class AbstractConstructorGeneratorPart<T : ConeLombokAnnotations.ConstructorAnnotation>(private val session: FirSession) {
|
||||
protected val lombokService: LombokService
|
||||
get() = session.lombokService
|
||||
|
||||
protected abstract fun getConstructorInfo(classSymbol: FirClassSymbol<*>): T?
|
||||
protected abstract fun getFieldsForParameters(classSymbol: FirClassSymbol<*>): List<FirJavaField>
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
fun createConstructor(classSymbol: FirClassSymbol<*>): FirFunction? {
|
||||
val constructorInfo = getConstructorInfo(classSymbol) ?: return null
|
||||
val staticName = constructorInfo.staticName?.let { Name.identifier(it) }
|
||||
|
||||
val substitutor: JavaTypeSubstitutor
|
||||
val builder = if (staticName == null) {
|
||||
FirJavaConstructorBuilder().apply {
|
||||
symbol = FirConstructorSymbol(classSymbol.classId.callableIdForConstructor())
|
||||
classSymbol.fir.typeParameters.mapTo(typeParameters) {
|
||||
buildConstructedClassTypeParameterRef { symbol = it.symbol }
|
||||
}
|
||||
substitutor = JavaTypeSubstitutor.Empty
|
||||
returnTypeRef = buildResolvedTypeRef {
|
||||
type = classSymbol.defaultType()
|
||||
}
|
||||
isInner = classSymbol.isInner
|
||||
isPrimary = false
|
||||
isFromSource = true
|
||||
annotationBuilder = { emptyList() }
|
||||
}
|
||||
} else {
|
||||
FirJavaMethodBuilder().apply {
|
||||
name = staticName
|
||||
val methodSymbol = FirNamedFunctionSymbol(CallableId(classSymbol.classId, staticName))
|
||||
symbol = methodSymbol
|
||||
|
||||
val classTypeParameterSymbols = classSymbol.fir.typeParameters.map { it.symbol }
|
||||
classTypeParameterSymbols.mapTo(typeParameters) {
|
||||
buildTypeParameterCopy(it.fir) {
|
||||
symbol = FirTypeParameterSymbol()
|
||||
containingDeclarationSymbol = methodSymbol
|
||||
}
|
||||
}
|
||||
|
||||
val javaClass = classSymbol.fir as FirJavaClass
|
||||
val javaTypeParametersFromClass = javaClass.javaTypeParameterStack
|
||||
.filter { it.value in classTypeParameterSymbols }
|
||||
.map { it.key }
|
||||
|
||||
val functionTypeParameterToJavaTypeParameter = typeParameters.zip(javaTypeParametersFromClass)
|
||||
.associate { (parameter, javaParameter) -> parameter.symbol to JavaTypeParameterStub(javaParameter) }
|
||||
|
||||
for ((parameter, javaParameter) in functionTypeParameterToJavaTypeParameter) {
|
||||
javaClass.javaTypeParameterStack.addParameter(javaParameter, parameter)
|
||||
}
|
||||
|
||||
val javaTypeSubstitution: Map<JavaClassifier, JavaType> = javaTypeParametersFromClass
|
||||
.zip(functionTypeParameterToJavaTypeParameter.values)
|
||||
.associate { (originalParameter, newParameter) ->
|
||||
originalParameter to JavaTypeParameterTypeStub(newParameter)
|
||||
}
|
||||
|
||||
substitutor = JavaTypeSubstitutorByMap(javaTypeSubstitution)
|
||||
returnTypeRef = buildResolvedTypeRef {
|
||||
type = classSymbol.classId.defaultType(functionTypeParameterToJavaTypeParameter.keys.toList())
|
||||
}
|
||||
|
||||
isStatic = true
|
||||
isFromSource = true
|
||||
annotationBuilder = { emptyList() }
|
||||
}
|
||||
}
|
||||
|
||||
builder.apply {
|
||||
moduleData = classSymbol.moduleData
|
||||
status = FirResolvedDeclarationStatusImpl(
|
||||
constructorInfo.visibility,
|
||||
Modality.FINAL,
|
||||
constructorInfo.visibility.toEffectiveVisibility(classSymbol)
|
||||
).apply {
|
||||
if (staticName != null) {
|
||||
isStatic = true
|
||||
}
|
||||
}
|
||||
|
||||
val fields = getFieldsForParameters(classSymbol)
|
||||
fields.mapTo(valueParameters) { field ->
|
||||
buildJavaValueParameter {
|
||||
moduleData = field.moduleData
|
||||
returnTypeRef = when (val typeRef = field.returnTypeRef) {
|
||||
is FirJavaTypeRef -> buildJavaTypeRef {
|
||||
type = substitutor.substituteOrSelf(typeRef.type)
|
||||
annotationBuilder = { emptyList() }
|
||||
}
|
||||
else -> typeRef
|
||||
}
|
||||
name = field.name
|
||||
annotationBuilder = { emptyList() }
|
||||
isVararg = false
|
||||
isFromSource = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build().apply {
|
||||
containingClassForStaticMemberAttr = classSymbol.toLookupTag()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class JavaTypeParameterStub(val original: JavaTypeParameter) : JavaTypeParameter {
|
||||
override val name: Name
|
||||
get() = original.name
|
||||
override val isFromSource: Boolean
|
||||
get() = true
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
get() = original.annotations
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = original.isDeprecatedInJavaDoc
|
||||
|
||||
override fun findAnnotation(fqName: FqName): JavaAnnotation? {
|
||||
return original.findAnnotation(fqName)
|
||||
}
|
||||
|
||||
override val upperBounds: Collection<JavaClassifierType>
|
||||
get() = original.upperBounds
|
||||
}
|
||||
|
||||
private class JavaClassifierTypeStub(
|
||||
val original: JavaClassifierType,
|
||||
override val typeArguments: List<JavaType?>,
|
||||
) : JavaClassifierType {
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
get() = original.annotations
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = original.isDeprecatedInJavaDoc
|
||||
override val classifier: JavaClassifier?
|
||||
get() = original.classifier
|
||||
override val isRaw: Boolean
|
||||
get() = original.isRaw
|
||||
override val classifierQualifiedName: String
|
||||
get() = original.classifierQualifiedName
|
||||
override val presentableText: String
|
||||
get() = original.presentableText
|
||||
}
|
||||
|
||||
private class JavaTypeParameterTypeStub(
|
||||
override val classifier: JavaTypeParameter
|
||||
) : JavaClassifierType {
|
||||
override val annotations: Collection<JavaAnnotation>
|
||||
get() = emptyList()
|
||||
override val isDeprecatedInJavaDoc: Boolean
|
||||
get() = false
|
||||
override val typeArguments: List<JavaType?>
|
||||
get() = emptyList()
|
||||
override val isRaw: Boolean
|
||||
get() = false
|
||||
override val classifierQualifiedName: String
|
||||
get() = classifier.name.identifier
|
||||
override val presentableText: String
|
||||
get() = classifierQualifiedName
|
||||
}
|
||||
|
||||
private sealed class JavaTypeSubstitutor {
|
||||
object Empty : JavaTypeSubstitutor() {
|
||||
override fun substituteOrNull(type: JavaType): JavaType? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun substituteOrSelf(type: JavaType): JavaType {
|
||||
return substituteOrNull(type) ?: type
|
||||
}
|
||||
|
||||
abstract fun substituteOrNull(type: JavaType): JavaType?
|
||||
}
|
||||
|
||||
private class JavaTypeSubstitutorByMap(val map: Map<JavaClassifier, JavaType>) : JavaTypeSubstitutor() {
|
||||
override fun substituteOrNull(type: JavaType): JavaType? {
|
||||
if (type !is JavaClassifierType) return null
|
||||
map[type.classifier]?.let { return it }
|
||||
var hasNewArguments = false
|
||||
val newArguments = type.typeArguments.map { argument ->
|
||||
if (argument == null) return@map null
|
||||
val newArgument = substituteOrNull(argument)
|
||||
if (newArgument !== argument) {
|
||||
hasNewArguments = true
|
||||
newArgument
|
||||
} else {
|
||||
argument
|
||||
}
|
||||
}
|
||||
return runIf(hasNewArguments) {
|
||||
JavaClassifierTypeStub(type, newArguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.generators
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.AllArgsConstructor
|
||||
|
||||
class AllArgsConstructorGeneratorPart(session: FirSession) : AbstractConstructorGeneratorPart<AllArgsConstructor>(session) {
|
||||
override fun getConstructorInfo(classSymbol: FirClassSymbol<*>): AllArgsConstructor? {
|
||||
return lombokService.getAllArgsConstructor(classSymbol)
|
||||
?: lombokService.getValue(classSymbol)?.asAllArgsConstructor()
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
override fun getFieldsForParameters(classSymbol: FirClassSymbol<*>): List<FirJavaField> {
|
||||
return classSymbol.fir.declarations.filterIsInstance<FirJavaField>()
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.generators
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
|
||||
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
|
||||
import org.jetbrains.kotlin.fir.java.declarations.buildJavaMethod
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.toEffectiveVisibility
|
||||
import org.jetbrains.kotlin.lombok.config.AccessLevel
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Accessors
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Getter
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokService
|
||||
import org.jetbrains.kotlin.lombok.k2.config.lombokService
|
||||
import org.jetbrains.kotlin.lombok.utils.AccessorNames
|
||||
import org.jetbrains.kotlin.lombok.utils.capitalize
|
||||
import org.jetbrains.kotlin.lombok.utils.collectWithNotNull
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class GetterGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {
|
||||
private val lombokService: LombokService
|
||||
get() = session.lombokService
|
||||
|
||||
private val cache: FirCache<FirClassSymbol<*>, Map<Name, FirJavaMethod>?, Nothing?> =
|
||||
session.firCachesFactory.createCache(::createGetters)
|
||||
|
||||
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
|
||||
if (!classSymbol.isSuitableJavaClass()) return emptySet()
|
||||
return cache.getValue(classSymbol)?.keys ?: emptySet()
|
||||
}
|
||||
|
||||
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
|
||||
val owner = context?.owner
|
||||
if (owner == null || !owner.isSuitableJavaClass()) return emptyList()
|
||||
val getter = cache.getValue(owner)?.get(callableId.callableName) ?: return emptyList()
|
||||
return listOf(getter.symbol)
|
||||
}
|
||||
|
||||
private fun createGetters(classSymbol: FirClassSymbol<*>): Map<Name, FirJavaMethod>? {
|
||||
val fieldsWithGetter = computeFieldsWithGetter(classSymbol) ?: return null
|
||||
val globalAccessors = lombokService.getAccessors(classSymbol)
|
||||
return fieldsWithGetter.mapNotNull { (field, getterInfo) ->
|
||||
val getterName = computeGetterName(field, getterInfo, globalAccessors) ?: return@mapNotNull null
|
||||
val function = buildJavaMethod {
|
||||
moduleData = field.moduleData
|
||||
returnTypeRef = field.returnTypeRef
|
||||
dispatchReceiverType = classSymbol.defaultType()
|
||||
name = getterName
|
||||
symbol = FirNamedFunctionSymbol(CallableId(classSymbol.classId, getterName))
|
||||
val visibility = getterInfo.visibility.toVisibility()
|
||||
status = FirResolvedDeclarationStatusImpl(visibility, Modality.OPEN, visibility.toEffectiveVisibility(classSymbol))
|
||||
isStatic = false
|
||||
isFromSource = true
|
||||
annotationBuilder = { emptyList() }
|
||||
}
|
||||
getterName to function
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun computeFieldsWithGetter(classSymbol: FirClassSymbol<*>): List<Pair<FirJavaField, Getter>>? {
|
||||
val classGetter = lombokService.getGetter(classSymbol)
|
||||
?: lombokService.getData(classSymbol)?.asGetter()
|
||||
?: lombokService.getValue(classSymbol)?.asGetter()
|
||||
|
||||
return classSymbol.fir.declarations
|
||||
.filterIsInstance<FirJavaField>()
|
||||
.collectWithNotNull { lombokService.getGetter(it.symbol) ?: classGetter }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
private fun computeGetterName(field: FirJavaField, getterInfo: Getter, globalAccessors: Accessors): Name? {
|
||||
if (getterInfo.visibility == AccessLevel.NONE) return null
|
||||
val accessors = lombokService.getAccessorsIfAnnotated(field.symbol) ?: globalAccessors
|
||||
val propertyName = field.toAccessorBaseName(accessors) ?: return null
|
||||
val functionName = if (accessors.fluent) {
|
||||
propertyName
|
||||
} else {
|
||||
val prefix = if (field.returnTypeRef.isPrimitiveBoolean() && !accessors.noIsPrefix) AccessorNames.IS else AccessorNames.GET
|
||||
prefix + propertyName.capitalize()
|
||||
}
|
||||
return Name.identifier(functionName)
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.generators
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
|
||||
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
|
||||
class LombokConstructorsGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {
|
||||
private val parts: List<AbstractConstructorGeneratorPart<*>> = listOf(
|
||||
AllArgsConstructorGeneratorPart(session),
|
||||
NoArgsConstructorGeneratorPart(session),
|
||||
RequiredArgsConstructorGeneratorPart(session)
|
||||
)
|
||||
|
||||
private val cache: FirCache<FirClassSymbol<*>, Collection<FirFunctionSymbol<*>>?, Nothing?> =
|
||||
session.firCachesFactory.createCache(::createConstructors)
|
||||
|
||||
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
|
||||
if (!classSymbol.isSuitableJavaClass()) return emptySet()
|
||||
return cache.getValue(classSymbol)?.mapTo(mutableSetOf()) {
|
||||
when (it) {
|
||||
is FirConstructorSymbol -> SpecialNames.INIT
|
||||
else -> it.callableId.callableName
|
||||
}
|
||||
} ?: emptySet()
|
||||
}
|
||||
|
||||
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
|
||||
val owner = context?.owner ?: return emptyList()
|
||||
if (!owner.isSuitableJavaClass()) return emptyList()
|
||||
return cache.getValue(owner)?.filterIsInstance<FirNamedFunctionSymbol>().orEmpty()
|
||||
}
|
||||
|
||||
override fun generateConstructors(context: MemberGenerationContext): List<FirConstructorSymbol> {
|
||||
val owner = context.owner
|
||||
if (!owner.isSuitableJavaClass()) return emptyList()
|
||||
return cache.getValue(owner)?.filterIsInstance<FirConstructorSymbol>().orEmpty()
|
||||
}
|
||||
|
||||
private fun createConstructors(classSymbol: FirClassSymbol<*>): Collection<FirFunctionSymbol<*>>? {
|
||||
return parts
|
||||
.mapNotNull { it.createConstructor(classSymbol) }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.filterClashingDeclarations(classSymbol)
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.generators
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.NoArgsConstructor
|
||||
|
||||
class NoArgsConstructorGeneratorPart(session: FirSession) : AbstractConstructorGeneratorPart<NoArgsConstructor>(session) {
|
||||
override fun getConstructorInfo(classSymbol: FirClassSymbol<*>): NoArgsConstructor? {
|
||||
return lombokService.getNoArgsConstructor(classSymbol)
|
||||
}
|
||||
|
||||
override fun getFieldsForParameters(classSymbol: FirClassSymbol<*>): List<FirJavaField> {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.generators
|
||||
|
||||
import com.intellij.psi.PsiField
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.expressions.classId
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.RequiredArgsConstructor
|
||||
import org.jetbrains.kotlin.lombok.utils.LombokNames
|
||||
import org.jetbrains.kotlin.psi
|
||||
|
||||
class RequiredArgsConstructorGeneratorPart(session: FirSession) : AbstractConstructorGeneratorPart<RequiredArgsConstructor>(session) {
|
||||
override fun getConstructorInfo(classSymbol: FirClassSymbol<*>): RequiredArgsConstructor? {
|
||||
return lombokService.getRequiredArgsConstructor(classSymbol)
|
||||
?: lombokService.getData(classSymbol)?.asRequiredArgsConstructor()
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
override fun getFieldsForParameters(classSymbol: FirClassSymbol<*>): List<FirJavaField> {
|
||||
return classSymbol.fir.declarations
|
||||
.filterIsInstance<FirJavaField>()
|
||||
.filter { it.isFieldRequired() }
|
||||
}
|
||||
|
||||
private fun FirJavaField.isFieldRequired(): Boolean {
|
||||
// TODO: consider adding `hasInitializer` property directly to java model
|
||||
val hasInitializer = (source?.psi as? PsiField)?.hasInitializer() ?: false
|
||||
if (hasInitializer) return false
|
||||
if (isVal) return true
|
||||
return annotations.any { it.classId?.asSingleFqName() in LombokNames.NON_NULL_ANNOTATIONS }
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.generators
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
|
||||
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
|
||||
import org.jetbrains.kotlin.fir.java.declarations.buildJavaMethod
|
||||
import org.jetbrains.kotlin.fir.java.declarations.buildJavaValueParameter
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.toEffectiveVisibility
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
import org.jetbrains.kotlin.lombok.config.AccessLevel
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Accessors
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Setter
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokService
|
||||
import org.jetbrains.kotlin.lombok.k2.config.lombokService
|
||||
import org.jetbrains.kotlin.lombok.utils.AccessorNames
|
||||
import org.jetbrains.kotlin.lombok.utils.capitalize
|
||||
import org.jetbrains.kotlin.lombok.utils.collectWithNotNull
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class SetterGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {
|
||||
private val lombokService: LombokService
|
||||
get() = session.lombokService
|
||||
|
||||
private val cache: FirCache<FirClassSymbol<*>, Map<Name, FirJavaMethod>?, Nothing?> =
|
||||
session.firCachesFactory.createCache(::createSetters)
|
||||
|
||||
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
|
||||
if (!classSymbol.isSuitableForSetters()) return emptySet()
|
||||
return cache.getValue(classSymbol)?.keys ?: emptySet()
|
||||
}
|
||||
|
||||
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
|
||||
val owner = context?.owner
|
||||
if (owner == null || !owner.isSuitableForSetters()) return emptyList()
|
||||
val getter = cache.getValue(owner)?.get(callableId.callableName) ?: return emptyList()
|
||||
return listOf(getter.symbol)
|
||||
}
|
||||
|
||||
private fun FirClassSymbol<*>.isSuitableForSetters(): Boolean {
|
||||
return isSuitableJavaClass() && classKind != ClassKind.ENUM_CLASS
|
||||
}
|
||||
|
||||
private fun createSetters(classSymbol: FirClassSymbol<*>): Map<Name, FirJavaMethod>? {
|
||||
val fieldsWithSetter = computeFieldsWithSetters(classSymbol) ?: return null
|
||||
val globalAccessors = lombokService.getAccessors(classSymbol)
|
||||
return fieldsWithSetter.mapNotNull { (field, setterInfo) ->
|
||||
val accessors = lombokService.getAccessorsIfAnnotated(field.symbol) ?: globalAccessors
|
||||
val setterName = computeSetterName(field, setterInfo, accessors) ?: return@mapNotNull null
|
||||
val function = buildJavaMethod {
|
||||
moduleData = field.moduleData
|
||||
returnTypeRef = if (accessors.chain) {
|
||||
buildResolvedTypeRef {
|
||||
type = classSymbol.defaultType()
|
||||
}
|
||||
} else {
|
||||
session.builtinTypes.unitType
|
||||
}
|
||||
|
||||
dispatchReceiverType = classSymbol.defaultType()
|
||||
name = setterName
|
||||
symbol = FirNamedFunctionSymbol(CallableId(classSymbol.classId, setterName))
|
||||
val visibility = setterInfo.visibility.toVisibility()
|
||||
status = FirResolvedDeclarationStatusImpl(visibility, Modality.OPEN, visibility.toEffectiveVisibility(classSymbol))
|
||||
|
||||
valueParameters += buildJavaValueParameter {
|
||||
moduleData = field.moduleData
|
||||
returnTypeRef = field.returnTypeRef
|
||||
name = field.name
|
||||
annotationBuilder = { emptyList() }
|
||||
isVararg = false
|
||||
isFromSource = true
|
||||
}
|
||||
|
||||
isStatic = false
|
||||
isFromSource = true
|
||||
annotationBuilder = { emptyList() }
|
||||
}
|
||||
setterName to function
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun computeFieldsWithSetters(classSymbol: FirClassSymbol<*>): List<Pair<FirJavaField, Setter>>? {
|
||||
val classSetter = lombokService.getSetter(classSymbol)
|
||||
?: lombokService.getData(classSymbol)?.asSetter()
|
||||
|
||||
return classSymbol.fir.declarations
|
||||
.filterIsInstance<FirJavaField>()
|
||||
.filter { it.isVar }
|
||||
.collectWithNotNull { lombokService.getSetter(it.symbol) ?: classSetter }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
private fun computeSetterName(field: FirJavaField, setterInfo: Setter, accessors: Accessors): Name? {
|
||||
if (setterInfo.visibility == AccessLevel.NONE) return null
|
||||
val propertyName = field.toAccessorBaseName(accessors) ?: return null
|
||||
val functionName = if (accessors.fluent) {
|
||||
propertyName
|
||||
} else {
|
||||
AccessorNames.SET + propertyName.capitalize()
|
||||
}
|
||||
return Name.identifier(functionName)
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.lombok.k2.generators
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
|
||||
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
|
||||
import org.jetbrains.kotlin.fir.java.declarations.buildJavaMethod
|
||||
import org.jetbrains.kotlin.fir.java.declarations.buildJavaValueParameter
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.toEffectiveVisibility
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
import org.jetbrains.kotlin.lombok.config.AccessLevel
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.With
|
||||
import org.jetbrains.kotlin.lombok.k2.config.LombokService
|
||||
import org.jetbrains.kotlin.lombok.k2.config.lombokService
|
||||
import org.jetbrains.kotlin.lombok.utils.collectWithNotNull
|
||||
import org.jetbrains.kotlin.lombok.utils.toPropertyNameCapitalized
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class WithGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {
|
||||
private val lombokService: LombokService
|
||||
get() = session.lombokService
|
||||
|
||||
private val cache: FirCache<FirClassSymbol<*>, Map<Name, FirJavaMethod>?, Nothing?> =
|
||||
session.firCachesFactory.createCache(::createWith)
|
||||
|
||||
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
|
||||
if (!classSymbol.isSuitableJavaClass()) return emptySet()
|
||||
return cache.getValue(classSymbol)?.keys ?: emptySet()
|
||||
}
|
||||
|
||||
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
|
||||
val owner = context?.owner
|
||||
if (owner == null || !owner.isSuitableJavaClass()) return emptyList()
|
||||
val getter = cache.getValue(owner)?.get(callableId.callableName) ?: return emptyList()
|
||||
return listOf(getter.symbol)
|
||||
}
|
||||
|
||||
private fun createWith(classSymbol: FirClassSymbol<*>): Map<Name, FirJavaMethod>? {
|
||||
val fieldsWithWith = computeFieldsWithWithAnnotation(classSymbol) ?: return null
|
||||
return fieldsWithWith.mapNotNull { (field, withInfo) ->
|
||||
val withName = computeWithName(field, withInfo) ?: return@mapNotNull null
|
||||
val function = buildJavaMethod {
|
||||
moduleData = field.moduleData
|
||||
returnTypeRef = buildResolvedTypeRef {
|
||||
type = classSymbol.defaultType()
|
||||
}
|
||||
|
||||
dispatchReceiverType = classSymbol.defaultType()
|
||||
name = withName
|
||||
symbol = FirNamedFunctionSymbol(CallableId(classSymbol.classId, withName))
|
||||
val visibility = withInfo.visibility.toVisibility()
|
||||
status = FirResolvedDeclarationStatusImpl(visibility, Modality.OPEN, visibility.toEffectiveVisibility(classSymbol))
|
||||
|
||||
valueParameters += buildJavaValueParameter {
|
||||
moduleData = field.moduleData
|
||||
returnTypeRef = field.returnTypeRef
|
||||
name = field.name
|
||||
annotationBuilder = { emptyList() }
|
||||
isVararg = false
|
||||
isFromSource = true
|
||||
}
|
||||
|
||||
isStatic = false
|
||||
isFromSource = true
|
||||
annotationBuilder = { emptyList() }
|
||||
}
|
||||
withName to function
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun computeFieldsWithWithAnnotation(classSymbol: FirClassSymbol<*>): List<Pair<FirJavaField, With>>? {
|
||||
val classWith = lombokService.getWith(classSymbol)
|
||||
|
||||
return classSymbol.fir.declarations
|
||||
.filterIsInstance<FirJavaField>()
|
||||
.filter { it.isVar }
|
||||
.collectWithNotNull { lombokService.getWith(it.symbol) ?: classWith }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
private fun computeWithName(field: FirJavaField, withInfo: With): Name? {
|
||||
if (withInfo.visibility == AccessLevel.NONE) return null
|
||||
val functionName = "with" + toPropertyNameCapitalized(field.name.identifier)
|
||||
return Name.identifier(functionName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.jetbrains.kotlin.lombok.k2.generators
|
||||
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.fir.declarations.FirConstructor
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType
|
||||
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations
|
||||
import org.jetbrains.kotlin.lombok.utils.AccessorNames
|
||||
import org.jetbrains.kotlin.lombok.utils.toPropertyName
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
fun FirJavaField.toAccessorBaseName(config: ConeLombokAnnotations.Accessors): String? {
|
||||
val isPrimitiveBoolean = returnTypeRef.isPrimitiveBoolean()
|
||||
return if (config.prefix.isEmpty()) {
|
||||
val prefixes = if (isPrimitiveBoolean) listOf(AccessorNames.IS) else emptyList()
|
||||
toPropertyName(name.identifier, prefixes)
|
||||
} else {
|
||||
val id = name.identifier
|
||||
val name = toPropertyName(id, config.prefix)
|
||||
name.takeIf { it.length != id.length}
|
||||
}
|
||||
}
|
||||
|
||||
fun FirTypeRef.isPrimitiveBoolean(): Boolean {
|
||||
return when (this) {
|
||||
is FirJavaTypeRef -> (type as? JavaPrimitiveType)?.type == PrimitiveType.BOOLEAN
|
||||
else -> this.coneTypeSafe<ConeKotlinType>()?.lowerBoundIfFlexible()?.isBoolean ?: false
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun FirClassSymbol<*>.isSuitableJavaClass(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isSuitableJavaClass is FirRegularClassSymbol)
|
||||
}
|
||||
return (this is FirRegularClassSymbol) && origin == FirDeclarationOrigin.Java.Source
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
fun List<FirFunction>.filterClashingDeclarations(classSymbol: FirClassSymbol<*>): List<FirFunctionSymbol<*>> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val allStaticFunctionsAndConstructors = classSymbol.fir.declarations.filterIsInstance<FirFunction>().toMutableList()
|
||||
val result = mutableListOf<FirFunction>()
|
||||
for (function in this) {
|
||||
if (allStaticFunctionsAndConstructors.none { sameSignature(it, function) }) {
|
||||
allStaticFunctionsAndConstructors += function
|
||||
result += function
|
||||
}
|
||||
}
|
||||
return result.map { it.symbol }
|
||||
}
|
||||
|
||||
/**
|
||||
* Lombok treat functions as having the same signature by arguments count only
|
||||
* Corresponding code in lombok - https://github.com/projectlombok/lombok/blob/v1.18.20/src/core/lombok/javac/handlers/JavacHandlerUtil.java#L752
|
||||
*/
|
||||
private fun sameSignature(a: FirFunction, b: FirFunction): Boolean {
|
||||
if (a is FirConstructor && b !is FirConstructor || a !is FirConstructor && b is FirConstructor) return false
|
||||
if (a.symbol.callableId.callableName != b.symbol.callableId.callableName) return false
|
||||
val aVararg = a.valueParameters.any { it.isVararg }
|
||||
val bVararg = b.valueParameters.any { it.isVararg }
|
||||
val aSize = a.valueParameters.size
|
||||
val bSize = b.valueParameters.size
|
||||
return aVararg && bVararg ||
|
||||
aVararg && bSize >= (aSize - 1) ||
|
||||
bVararg && aSize >= (bSize - 1) ||
|
||||
aSize == bSize
|
||||
}
|
||||
Reference in New Issue
Block a user