[Lombok] Add implementation of plugin for FIR

This commit is contained in:
Dmitriy Novozhilov
2022-05-25 16:18:50 +03:00
committed by teamcity
parent e58e86932c
commit a84ece7233
41 changed files with 1685 additions and 42 deletions
+28
View File
@@ -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()
@@ -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"
}
@@ -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)
}
}
}
@@ -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>()
}
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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()
}
}
@@ -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 }
}
}
@@ -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)
}
}
@@ -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
}