[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
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.dispatchReceiverClassOrNull
import org.jetbrains.kotlin.fir.isNewPlaceForBodyGeneration
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
import org.jetbrains.kotlin.fir.scopes.getDeclaredConstructors
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
@@ -8,8 +8,7 @@ package org.jetbrains.kotlin.fir.java
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
internal class JavaTypeParameterStack {
class JavaTypeParameterStack : Iterable<Map.Entry<JavaTypeParameter, FirTypeParameterSymbol>> {
private val typeParameterMap = mutableMapOf<JavaTypeParameter, FirTypeParameterSymbol>()
fun addParameter(javaTypeParameter: JavaTypeParameter, symbol: FirTypeParameterSymbol) {
@@ -25,6 +24,10 @@ internal class JavaTypeParameterStack {
?: throw IllegalArgumentException("Cannot find Java type parameter $javaTypeParameter in stack")
}
override operator fun iterator(): Iterator<Map.Entry<JavaTypeParameter, FirTypeParameterSymbol>> {
return typeParameterMap.iterator()
}
companion object {
val EMPTY: JavaTypeParameterStack = JavaTypeParameterStack()
}
@@ -45,7 +45,7 @@ class FirJavaClass @FirImplementationDetail internal constructor(
override val superTypeRefs: MutableList<FirTypeRef>,
override val typeParameters: MutableList<FirTypeParameterRef>,
internal val javaPackage: JavaPackage?,
internal val javaTypeParameterStack: JavaTypeParameterStack,
val javaTypeParameterStack: JavaTypeParameterStack,
internal val existingNestedClassifierNames: List<Name>
) : FirRegularClass() {
override val hasLazyNestedClassifiers: Boolean get() = true
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeSimpleKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.fir.visitors.transformInplace
@@ -240,7 +239,7 @@ class FirJavaMethodBuilder : FirFunctionBuilder, FirTypeParametersOwnerBuilder,
origin = javaOrigin(isFromSource),
resolvePhase,
attributes,
returnTypeRef as FirJavaTypeRef,
returnTypeRef,
typeParameters,
valueParameters,
name,
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private fun FirAnnotation.toAnnotationLookupTag(): ConeClassLikeLookupTag? =
// this cast fails when we have generic-typed annotations @T
@@ -159,10 +158,19 @@ fun FirAnnotation.findArgumentByName(name: Name): FirExpression? {
return arguments.singleOrNull()
}
fun FirAnnotation.getStringArgument(name: Name): String? =
findArgumentByName(name)?.let { expression ->
expression.safeAs<FirConstExpression<*>>()?.value as? String
fun FirAnnotation.getBooleanArgument(name: Name): Boolean? = getPrimitiveArgumentValue(name)
fun FirAnnotation.getStringArgument(name: Name): String? = getPrimitiveArgumentValue(name)
fun FirAnnotation.getIntArgument(name: Name): Int? = getPrimitiveArgumentValue(name)
fun FirAnnotation.getStringArrayArgument(name: Name): List<String>? {
val argument = findArgumentByName(name) as? FirArrayOfCall ?: return null
return argument.arguments.mapNotNull { (it as? FirConstExpression<*>)?.value as? String }
}
private inline fun <reified T> FirAnnotation.getPrimitiveArgumentValue(name: Name): T? {
return findArgumentByName(name)?.let { expression ->
(expression as? FirConstExpression<*>)?.value as? T
}
}
fun FirAnnotationContainer.getJvmNameFromAnnotation(target: AnnotationUseSiteTarget? = null): String? {
val annotationCalls = getAnnotationsByClassId(StandardClassIds.Annotations.JvmName)
@@ -53,7 +53,6 @@ object FirGeneratedElementsValidator : FirDefaultVisitor<Unit, Any?>() {
}
override fun visitTypeRef(typeRef: FirTypeRef, data: Any?) {
require(typeRef is FirResolvedTypeRef)
typeRef.acceptChildren(this, null)
}
@@ -29,9 +29,7 @@ import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTes
import org.jetbrains.kotlin.kapt3.test.AbstractIrClassFileToSourceStubConverterTest
import org.jetbrains.kotlin.kapt3.test.AbstractIrKotlinKaptContextTest
import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest
import org.jetbrains.kotlin.lombok.AbstractBlackBoxCodegenTestForLombok
import org.jetbrains.kotlin.lombok.AbstractDiagnosticTestForLombok
import org.jetbrains.kotlin.lombok.AbstractIrBlackBoxCodegenTestForLombok
import org.jetbrains.kotlin.lombok.*
import org.jetbrains.kotlin.noarg.*
import org.jetbrains.kotlin.parcelize.test.runners.*
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
@@ -430,8 +428,14 @@ fun main(args: Array<String>) {
testClass<AbstractIrBlackBoxCodegenTestForLombok> {
model("box")
}
testClass<AbstractFirBlackBoxCodegenTestForLombok> {
model("box")
}
testClass<AbstractDiagnosticTestForLombok> {
model("diagnostics")
model("diagnostics", excludedPattern = excludedFirTestdataPattern)
}
testClass<AbstractFirDiagnosticTestForLombok> {
model("diagnostics", excludedPattern = excludedFirTestdataPattern)
}
}
}
+2
View File
@@ -8,11 +8,13 @@ plugins {
dependencies {
embedded(project(":kotlin-lombok-compiler-plugin.common"))
embedded(project(":kotlin-lombok-compiler-plugin.k1"))
embedded(project(":kotlin-lombok-compiler-plugin.k2"))
embedded(project(":kotlin-lombok-compiler-plugin.cli"))
testImplementation(intellijCore())
testImplementation(project(":kotlin-lombok-compiler-plugin.common"))
testImplementation(project(":kotlin-lombok-compiler-plugin.k1"))
testImplementation(project(":kotlin-lombok-compiler-plugin.k2"))
testImplementation(project(":kotlin-lombok-compiler-plugin.cli"))
testImplementation("org.projectlombok:lombok:1.18.16")
@@ -11,9 +11,11 @@ dependencies {
compileOnly(project(":compiler:plugin-api"))
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:frontend.java"))
compileOnly(project(":compiler:fir:entrypoint"))
implementation(project(":kotlin-lombok-compiler-plugin.common"))
implementation(project(":kotlin-lombok-compiler-plugin.k1"))
implementation(project(":kotlin-lombok-compiler-plugin.k2"))
compileOnly(intellijCore())
}
@@ -15,17 +15,20 @@ import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
import org.jetbrains.kotlin.lombok.LombokConfigurationKeys.CONFIG_FILE
import org.jetbrains.kotlin.lombok.k2.FirLombokRegistrar
import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension
import java.io.File
import java.lang.IllegalArgumentException
class LombokComponentRegistrar : ComponentRegistrar {
companion object {
fun registerComponents(project: Project, compilerConfiguration: CompilerConfiguration) {
val config = LombokPluginConfig(compilerConfiguration[CONFIG_FILE])
val configFile = compilerConfiguration[CONFIG_FILE]
val config = LombokPluginConfig(configFile)
SyntheticJavaResolveExtension.registerExtension(project, LombokResolveExtension(config))
FirExtensionRegistrar.registerExtension(project, FirLombokRegistrar(configFile))
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.lombok.utils
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
object LombokNames {
@@ -20,6 +21,16 @@ object LombokNames {
val REQUIRED_ARGS_CONSTRUCTOR = FqName("lombok.RequiredArgsConstructor")
val ACCESSORS_ID = ClassId.topLevel(ACCESSORS)
val GETTER_ID = ClassId.topLevel(GETTER)
val SETTER_ID = ClassId.topLevel(SETTER)
val WITH_ID = ClassId.topLevel(WITH)
val DATA_ID = ClassId.topLevel(DATA)
val VALUE_ID = ClassId.topLevel(VALUE)
val NO_ARGS_CONSTRUCTOR_ID = ClassId.topLevel(NO_ARGS_CONSTRUCTOR)
val ALL_ARGS_CONSTRUCTOR_ID = ClassId.topLevel(ALL_ARGS_CONSTRUCTOR)
val REQUIRED_ARGS_CONSTRUCTOR_ID = ClassId.topLevel(REQUIRED_ARGS_CONSTRUCTOR)
//taken from idea lombok plugin
val NON_NULL_ANNOTATIONS = listOf(
"androidx.annotation.NonNull",
@@ -5,31 +5,12 @@
package org.jetbrains.kotlin.lombok.utils
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Accessors
object AccessorNames {
const val IS = "is"
const val GET = "get"
const val SET = "set"
}
/**
* Make property name from variable name
* Returns null in case getter/setter shouldn't be generated at all
*/
fun PropertyDescriptor.toAccessorBaseName(config: Accessors): String? {
val isPrimitiveBoolean = type.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 toPropertyName(name: String, prefixesToStrip: List<String> = emptyList()): String =
name.stripPrefixes(prefixesToStrip).decapitalize()
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.lombok.utils
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Accessors
/**
* Make property name from variable name
* Returns null in case getter/setter shouldn't be generated at all
*/
fun PropertyDescriptor.toAccessorBaseName(config: Accessors): String? {
val isPrimitiveBoolean = type.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}
}
}
+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
}
+7 -2
View File
@@ -1,6 +1,10 @@
// FULL_JDK
// WITH_STDLIB
// FILE: DataExample.java
import lombok.*;
import java.util.List;
import java.util.Arrays;
@Data public class DataExample {
private final String name;
@@ -13,6 +17,7 @@ import lombok.*;
public static class Exercise<T> {
private final String name;
private final T value;
private final List<T> list;
}
public static void usage() {
@@ -21,7 +26,7 @@ import lombok.*;
obj.getTags();
obj.setScore(1.5);
Exercise<Integer> ex = Exercise.of("name", 12);
Exercise<Integer> ex = Exercise.of("name", 12, Arrays.asList(1, 2, 3));
}
}
@@ -38,6 +43,6 @@ fun box(): String {
obj.score = 2.5
assertEquals(obj.score, 2.5)
val ex: DataExample.Exercise<Int> = DataExample.Exercise.of("name", 12)
val ex: DataExample.Exercise<Int> = DataExample.Exercise.of("name", 12, listOf(1, 2, 3))
return "OK"
}
@@ -0,0 +1,27 @@
// KT-46529
// FILE: PrefixJava.java
import lombok.*;
import lombok.experimental.*;
@Getter @Setter @Accessors(chain = false, fluent = true, prefix = {"pxo"})
public class PrefixJava {
private String pxaPropA = "A";
@Accessors(chain = true) private String pxoPropC = "C";
@Accessors private String pxaPropD = "D";
}
// FILE: test.kt
fun test() {
//not generated because doesn't have prefix from class level @Accessors
assertEquals(<!ARGUMENT_TYPE_MISMATCH!>PrefixJava().<!UNRESOLVED_REFERENCE!>propA<!><!>, "A")
//not generated because doesn't have prefix from config
assertEquals(<!ARGUMENT_TYPE_MISMATCH!>PrefixJava().<!UNRESOLVED_REFERENCE!>propC<!><!>, "C")
assertEquals(PrefixJava().propD, "D")
}
// FILE: lombok.config
lombok.accessors.prefix += pxa
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// KT-46529
// FILE: PrefixJava.java
+53
View File
@@ -0,0 +1,53 @@
// FILE: GetterTest.java
import lombok.AccessLevel;
import lombok.Getter;
public class GetterTest {
@Getter private int age = 10;
@Getter(AccessLevel.PROTECTED) private String name;
@Getter private boolean primitiveBoolean;
@Getter private Boolean boxedBoolean;
@Getter(AccessLevel.NONE) private Boolean invisible;
void test() {
getAge();
isPrimitiveBoolean();
// getInvisible();
}
}
// FILE: test.kt
fun test() {
val obj = GetterTest()
val getter = obj.getAge()
val property = obj.age
//todo kotlin doesn't seee isBoolean methods as property
obj.<!INVISIBLE_REFERENCE!>primitiveBoolean<!>
obj.isPrimitiveBoolean()
obj.boxedBoolean
obj.getBoxedBoolean()
//shouldn't be accesible from here
obj.<!INVISIBLE_REFERENCE!>getName<!>()
obj.<!UNRESOLVED_REFERENCE!>getInvisible<!>()
OverridenGetterTest().usage()
}
class OverridenGetterTest : GetterTest() {
fun usage() {
getName()
}
}
-1
View File
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// FILE: GetterTest.java
import lombok.AccessLevel;
@@ -0,0 +1,46 @@
// FILE: ClassLevelGetterTest.java
import lombok.AccessLevel;
import lombok.Getter;
@Getter
public class ClassLevelGetterTest {
private int age = 10;
@Getter(AccessLevel.PROTECTED) private String name;
private boolean primitiveBoolean;
private Boolean boxedBoolean;
void test() {
getAge();
isPrimitiveBoolean();
}
}
// FILE: test.kt
fun test() {
val obj = ClassLevelGetterTest()
val getter = obj.getAge()
val property = obj.age
obj.isPrimitiveBoolean()
obj.boxedBoolean
obj.getBoxedBoolean()
//shouldn't be accesible from here
obj.<!INVISIBLE_REFERENCE!>getName<!>()
OverridenGetterTest().usage()
}
class OverridenGetterTest : ClassLevelGetterTest() {
fun usage() {
getName()
}
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// FILE: ClassLevelGetterTest.java
import lombok.AccessLevel;
+42
View File
@@ -0,0 +1,42 @@
// FILE: SetterTest.java
import lombok.AccessLevel;
import lombok.Setter;
import lombok.Getter;
public class SetterTest {
@Getter @Setter private int age = 10;
@Setter(AccessLevel.PROTECTED) private String name;
@Setter private boolean primitiveBoolean;
void test() {
setAge(12);
setPrimitiveBoolean(true);
}
}
// FILE: test.kt
fun test() {
val obj = SetterTest()
obj.setAge(42)
obj.age = 42
//synthetic property generated only when there is a getter
obj.<!INVISIBLE_REFERENCE!>primitiveBoolean<!> = false
obj.setPrimitiveBoolean(true)
//shouldn't be accesible from here
obj.<!INVISIBLE_REFERENCE!>setName<!>("abc")
OverridenGetterTest().usage()
}
class OverridenGetterTest : SetterTest() {
fun usage() {
setName("abc")
}
}
-1
View File
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// FILE: SetterTest.java
import lombok.AccessLevel;
@@ -27,7 +27,7 @@ public class DiagnosticTestForLombokGenerated extends AbstractDiagnosticTestForL
@Test
public void testAllFilesPresentInDiagnostics() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/lombok/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), null, true);
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/lombok/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@@ -0,0 +1,165 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.lombok;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("plugins/lombok/testData/box")
@TestDataPath("$PROJECT_ROOT")
public class FirBlackBoxCodegenTestForLombokGenerated extends AbstractFirBlackBoxCodegenTestForLombok {
@Test
@TestMetadata("accessorsStripPrefix.kt")
public void testAccessorsStripPrefix() throws Exception {
runTest("plugins/lombok/testData/box/accessorsStripPrefix.kt");
}
@Test
@TestMetadata("accessorsStripPrefixConfig.kt")
public void testAccessorsStripPrefixConfig() throws Exception {
runTest("plugins/lombok/testData/box/accessorsStripPrefixConfig.kt");
}
@Test
@TestMetadata("allArgsConstructor.kt")
public void testAllArgsConstructor() throws Exception {
runTest("plugins/lombok/testData/box/allArgsConstructor.kt");
}
@Test
@TestMetadata("allArgsConstructorStatic.kt")
public void testAllArgsConstructorStatic() throws Exception {
runTest("plugins/lombok/testData/box/allArgsConstructorStatic.kt");
}
@Test
public void testAllFilesPresentInBox() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/lombok/testData/box"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("configAccessors.kt")
public void testConfigAccessors() throws Exception {
runTest("plugins/lombok/testData/box/configAccessors.kt");
}
@Test
@TestMetadata("configAccessorsOverride.kt")
public void testConfigAccessorsOverride() throws Exception {
runTest("plugins/lombok/testData/box/configAccessorsOverride.kt");
}
@Test
@TestMetadata("configCaseInsensitive.kt")
public void testConfigCaseInsensitive() throws Exception {
runTest("plugins/lombok/testData/box/configCaseInsensitive.kt");
}
@Test
@TestMetadata("configSimple.kt")
public void testConfigSimple() throws Exception {
runTest("plugins/lombok/testData/box/configSimple.kt");
}
@Test
@TestMetadata("data.kt")
public void testData() throws Exception {
runTest("plugins/lombok/testData/box/data.kt");
}
@Test
@TestMetadata("genericsAccessors.kt")
public void testGenericsAccessors() throws Exception {
runTest("plugins/lombok/testData/box/genericsAccessors.kt");
}
@Test
@TestMetadata("genericsConstructors.kt")
public void testGenericsConstructors() throws Exception {
runTest("plugins/lombok/testData/box/genericsConstructors.kt");
}
@Test
@TestMetadata("genericsConstructorsStatic.kt")
public void testGenericsConstructorsStatic() throws Exception {
runTest("plugins/lombok/testData/box/genericsConstructorsStatic.kt");
}
@Test
@TestMetadata("gettersFluent.kt")
public void testGettersFluent() throws Exception {
runTest("plugins/lombok/testData/box/gettersFluent.kt");
}
@Test
@TestMetadata("noArgsConstructor.kt")
public void testNoArgsConstructor() throws Exception {
runTest("plugins/lombok/testData/box/noArgsConstructor.kt");
}
@Test
@TestMetadata("noArgsConstructorStatic.kt")
public void testNoArgsConstructorStatic() throws Exception {
runTest("plugins/lombok/testData/box/noArgsConstructorStatic.kt");
}
@Test
@TestMetadata("nullability.kt")
public void testNullability() throws Exception {
runTest("plugins/lombok/testData/box/nullability.kt");
}
@Test
@TestMetadata("propertyFromSuper.kt")
public void testPropertyFromSuper() throws Exception {
runTest("plugins/lombok/testData/box/propertyFromSuper.kt");
}
@Test
@TestMetadata("requiredArgsConstructor.kt")
public void testRequiredArgsConstructor() throws Exception {
runTest("plugins/lombok/testData/box/requiredArgsConstructor.kt");
}
@Test
@TestMetadata("requiredArgsConstructorStatic.kt")
public void testRequiredArgsConstructorStatic() throws Exception {
runTest("plugins/lombok/testData/box/requiredArgsConstructorStatic.kt");
}
@Test
@TestMetadata("settersVariations.kt")
public void testSettersVariations() throws Exception {
runTest("plugins/lombok/testData/box/settersVariations.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("plugins/lombok/testData/box/simple.kt");
}
@Test
@TestMetadata("value.kt")
public void testValue() throws Exception {
runTest("plugins/lombok/testData/box/value.kt");
}
@Test
@TestMetadata("with.kt")
public void testWith() throws Exception {
runTest("plugins/lombok/testData/box/with.kt");
}
}
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.lombok;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("plugins/lombok/testData/diagnostics")
@TestDataPath("$PROJECT_ROOT")
public class FirDiagnosticTestForLombokGenerated extends AbstractFirDiagnosticTestForLombok {
@Test
@TestMetadata("accessorsStripPrefixCombined.kt")
public void testAccessorsStripPrefixCombined() throws Exception {
runTest("plugins/lombok/testData/diagnostics/accessorsStripPrefixCombined.kt");
}
@Test
public void testAllFilesPresentInDiagnostics() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/lombok/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("annotationTypes.kt")
public void testAnnotationTypes() throws Exception {
runTest("plugins/lombok/testData/diagnostics/annotationTypes.kt");
}
@Test
@TestMetadata("clashAccessors.kt")
public void testClashAccessors() throws Exception {
runTest("plugins/lombok/testData/diagnostics/clashAccessors.kt");
}
@Test
@TestMetadata("getters.kt")
public void testGetters() throws Exception {
runTest("plugins/lombok/testData/diagnostics/getters.kt");
}
@Test
@TestMetadata("gettersClassLevel.kt")
public void testGettersClassLevel() throws Exception {
runTest("plugins/lombok/testData/diagnostics/gettersClassLevel.kt");
}
@Test
@TestMetadata("setters.kt")
public void testSetters() throws Exception {
runTest("plugins/lombok/testData/diagnostics/setters.kt");
}
@Test
@TestMetadata("settersClassLevel.kt")
public void testSettersClassLevel() throws Exception {
runTest("plugins/lombok/testData/diagnostics/settersClassLevel.kt");
}
}
@@ -14,8 +14,11 @@ import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.runners.AbstractDiagnosticTest
import org.jetbrains.kotlin.test.runners.AbstractFirDiagnosticTest
import org.jetbrains.kotlin.test.runners.codegen.AbstractBlackBoxCodegenTest
import org.jetbrains.kotlin.test.runners.codegen.AbstractFirBlackBoxCodegenTest
import org.jetbrains.kotlin.test.runners.codegen.AbstractIrBlackBoxCodegenTest
import org.jetbrains.kotlin.test.runners.configurationForClassicAndFirTestsAlongside
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
@@ -36,6 +39,13 @@ open class AbstractIrBlackBoxCodegenTestForLombok : AbstractIrBlackBoxCodegenTes
}
}
open class AbstractFirBlackBoxCodegenTestForLombok : AbstractFirBlackBoxCodegenTest() {
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
builder.enableLombok()
}
}
// ---------------------------- diagnostics ----------------------------
open class AbstractDiagnosticTestForLombok : AbstractDiagnosticTest() {
@@ -45,6 +55,14 @@ open class AbstractDiagnosticTestForLombok : AbstractDiagnosticTest() {
}
}
open class AbstractFirDiagnosticTestForLombok : AbstractFirDiagnosticTest() {
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
builder.configurationForClassicAndFirTestsAlongside()
builder.enableLombok()
}
}
// ---------------------------- configuration ----------------------------
fun TestConfigurationBuilder.enableLombok() {
+2
View File
@@ -355,6 +355,7 @@ include ":plugins:parcelize:parcelize-compiler",
include ":kotlin-lombok-compiler-plugin",
":kotlin-lombok-compiler-plugin.common",
":kotlin-lombok-compiler-plugin.k1",
":kotlin-lombok-compiler-plugin.k2",
":kotlin-lombok-compiler-plugin.cli",
":kotlin-lombok"
@@ -699,6 +700,7 @@ project(':kotlin-allopen-compiler-plugin.k2').projectDir = "$rootDir/plugins/all
project(':kotlin-lombok-compiler-plugin').projectDir = "$rootDir/plugins/lombok" as File
project(':kotlin-lombok-compiler-plugin.cli').projectDir = "$rootDir/plugins/lombok/lombok.cli" as File
project(':kotlin-lombok-compiler-plugin.k1').projectDir = "$rootDir/plugins/lombok/lombok.k1" as File
project(':kotlin-lombok-compiler-plugin.k2').projectDir = "$rootDir/plugins/lombok/lombok.k2" as File
project(':kotlin-lombok-compiler-plugin.common').projectDir = "$rootDir/plugins/lombok/lombok.common" as File
project(':kotlin-noarg-compiler-plugin').projectDir = "$rootDir/plugins/noarg" as File