From a84ece723367a632b71a700ba4c2255b96d39ebc Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Wed, 25 May 2022 16:18:50 +0300 Subject: [PATCH] [Lombok] Add implementation of plugin for FIR --- .../kotlin/fir/lazy/Fir2IrLazyClass.kt | 2 + .../kotlin/fir/java/JavaTypeParameterStack.kt | 7 +- .../fir/java/declarations/FirJavaClass.kt | 2 +- .../fir/java/declarations/FirJavaMethod.kt | 3 +- .../fir/declarations/FirAnnotationUtils.kt | 16 +- .../GeneratedDeclarationValidation.kt | 1 - .../kotlin/generators/tests/GenerateTests.kt | 12 +- plugins/lombok/build.gradle.kts | 2 + plugins/lombok/lombok.cli/build.gradle.kts | 2 + .../jetbrains/kotlin/lombok/LombokPlugin.kt | 7 +- .../kotlin/lombok/utils/LombokNames.kt | 11 + .../kotlin/lombok/utils/nameUtils.kt | 19 -- .../kotlin/lombok/utils/K1NameUtils.kt | 25 ++ plugins/lombok/lombok.k2/build.gradle.kts | 28 +++ .../kotlin/lombok/k2/FirLombokRegistrar.kt | 21 ++ .../kotlin/lombok/k2/config/LombokService.kt | 94 +++++++ .../lombok/k2/config/annotationConfig.kt | 193 +++++++++++++++ .../lombok/k2/config/annotationUtils.kt | 61 +++++ .../AbstractConstructorGeneratorPart.kt | 231 ++++++++++++++++++ .../AllArgsConstructorGeneratorPart.kt | 24 ++ .../lombok/k2/generators/GetterGenerator.kt | 100 ++++++++ .../generators/LombokConstructorsGenerator.kt | 61 +++++ .../NoArgsConstructorGeneratorPart.kt | 21 ++ .../RequiredArgsConstructorGeneratorPart.kt | 38 +++ .../lombok/k2/generators/SetterGenerator.kt | 123 ++++++++++ .../lombok/k2/generators/WithGenerator.kt | 104 ++++++++ .../kotlin/lombok/k2/generators/utils.kt | 83 +++++++ plugins/lombok/testData/box/data.kt | 9 +- .../accessorsStripPrefixCombined.fir.kt | 27 ++ .../accessorsStripPrefixCombined.kt | 1 - .../testData/diagnostics/getters.fir.kt | 53 ++++ .../lombok/testData/diagnostics/getters.kt | 1 - .../diagnostics/gettersClassLevel.fir.kt | 46 ++++ .../testData/diagnostics/gettersClassLevel.kt | 1 - .../testData/diagnostics/setters.fir.kt | 42 ++++ .../lombok/testData/diagnostics/setters.kt | 1 - .../DiagnosticTestForLombokGenerated.java | 2 +- ...BlackBoxCodegenTestForLombokGenerated.java | 165 +++++++++++++ .../FirDiagnosticTestForLombokGenerated.java | 68 ++++++ .../jetbrains/kotlin/lombok/LombokTests.kt | 18 ++ settings.gradle | 2 + 41 files changed, 1685 insertions(+), 42 deletions(-) rename plugins/lombok/{lombok.k1 => lombok.common}/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt (57%) create mode 100644 plugins/lombok/lombok.k1/src/org/jetbrains/kotlin/lombok/utils/K1NameUtils.kt create mode 100644 plugins/lombok/lombok.k2/build.gradle.kts create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/FirLombokRegistrar.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/LombokService.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/annotationConfig.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/annotationUtils.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/AbstractConstructorGeneratorPart.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/AllArgsConstructorGeneratorPart.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/GetterGenerator.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/LombokConstructorsGenerator.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/NoArgsConstructorGeneratorPart.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/RequiredArgsConstructorGeneratorPart.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/SetterGenerator.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/WithGenerator.kt create mode 100644 plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/utils.kt create mode 100644 plugins/lombok/testData/diagnostics/accessorsStripPrefixCombined.fir.kt create mode 100644 plugins/lombok/testData/diagnostics/getters.fir.kt create mode 100644 plugins/lombok/testData/diagnostics/gettersClassLevel.fir.kt create mode 100644 plugins/lombok/testData/diagnostics/setters.fir.kt create mode 100644 plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/FirBlackBoxCodegenTestForLombokGenerated.java create mode 100644 plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/FirDiagnosticTestForLombokGenerated.java diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt index 847e792a62a..d880635d6dc 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt @@ -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 diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeParameterStack.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeParameterStack.kt index 452851ee7ea..1cfbc2293a7 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeParameterStack.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeParameterStack.kt @@ -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> { private val typeParameterMap = mutableMapOf() 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> { + return typeParameterMap.iterator() + } + companion object { val EMPTY: JavaTypeParameterStack = JavaTypeParameterStack() } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaClass.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaClass.kt index 9a3bcbd3ee7..50cb0c88292 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaClass.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaClass.kt @@ -45,7 +45,7 @@ class FirJavaClass @FirImplementationDetail internal constructor( override val superTypeRefs: MutableList, override val typeParameters: MutableList, internal val javaPackage: JavaPackage?, - internal val javaTypeParameterStack: JavaTypeParameterStack, + val javaTypeParameterStack: JavaTypeParameterStack, internal val existingNestedClassifierNames: List ) : FirRegularClass() { override val hasLazyNestedClassifiers: Boolean get() = true diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaMethod.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaMethod.kt index 0067bf99e4e..b9ffe27f2c7 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaMethod.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/declarations/FirJavaMethod.kt @@ -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, diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt index f380f219ee5..5ea25a5309d 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt @@ -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>()?.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? { + val argument = findArgumentByName(name) as? FirArrayOfCall ?: return null + return argument.arguments.mapNotNull { (it as? FirConstExpression<*>)?.value as? String } +} + +private inline fun 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) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/GeneratedDeclarationValidation.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/GeneratedDeclarationValidation.kt index 56a503b3c5a..b96118d17a8 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/GeneratedDeclarationValidation.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/GeneratedDeclarationValidation.kt @@ -53,7 +53,6 @@ object FirGeneratedElementsValidator : FirDefaultVisitor() { } override fun visitTypeRef(typeRef: FirTypeRef, data: Any?) { - require(typeRef is FirResolvedTypeRef) typeRef.acceptChildren(this, null) } diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index af4fc15a3c6..e032c7e3138 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -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) { testClass { model("box") } + testClass { + model("box") + } testClass { - model("diagnostics") + model("diagnostics", excludedPattern = excludedFirTestdataPattern) + } + testClass { + model("diagnostics", excludedPattern = excludedFirTestdataPattern) } } } diff --git a/plugins/lombok/build.gradle.kts b/plugins/lombok/build.gradle.kts index fb617e346e1..a90d5222511 100644 --- a/plugins/lombok/build.gradle.kts +++ b/plugins/lombok/build.gradle.kts @@ -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") diff --git a/plugins/lombok/lombok.cli/build.gradle.kts b/plugins/lombok/lombok.cli/build.gradle.kts index 29af743bda4..edcf121d288 100644 --- a/plugins/lombok/lombok.cli/build.gradle.kts +++ b/plugins/lombok/lombok.cli/build.gradle.kts @@ -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()) } diff --git a/plugins/lombok/lombok.cli/src/org/jetbrains/kotlin/lombok/LombokPlugin.kt b/plugins/lombok/lombok.cli/src/org/jetbrains/kotlin/lombok/LombokPlugin.kt index fccea967032..38f0166497c 100644 --- a/plugins/lombok/lombok.cli/src/org/jetbrains/kotlin/lombok/LombokPlugin.kt +++ b/plugins/lombok/lombok.cli/src/org/jetbrains/kotlin/lombok/LombokPlugin.kt @@ -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)) } } diff --git a/plugins/lombok/lombok.common/src/org/jetbrains/kotlin/lombok/utils/LombokNames.kt b/plugins/lombok/lombok.common/src/org/jetbrains/kotlin/lombok/utils/LombokNames.kt index 0ab602e1c8d..4d2a4c1a3af 100644 --- a/plugins/lombok/lombok.common/src/org/jetbrains/kotlin/lombok/utils/LombokNames.kt +++ b/plugins/lombok/lombok.common/src/org/jetbrains/kotlin/lombok/utils/LombokNames.kt @@ -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", diff --git a/plugins/lombok/lombok.k1/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt b/plugins/lombok/lombok.common/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt similarity index 57% rename from plugins/lombok/lombok.k1/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt rename to plugins/lombok/lombok.common/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt index af9a46851cb..3044e5230e8 100644 --- a/plugins/lombok/lombok.k1/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt +++ b/plugins/lombok/lombok.common/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt @@ -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 = emptyList()): String = name.stripPrefixes(prefixesToStrip).decapitalize() diff --git a/plugins/lombok/lombok.k1/src/org/jetbrains/kotlin/lombok/utils/K1NameUtils.kt b/plugins/lombok/lombok.k1/src/org/jetbrains/kotlin/lombok/utils/K1NameUtils.kt new file mode 100644 index 00000000000..e1381002c14 --- /dev/null +++ b/plugins/lombok/lombok.k1/src/org/jetbrains/kotlin/lombok/utils/K1NameUtils.kt @@ -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} + } +} diff --git a/plugins/lombok/lombok.k2/build.gradle.kts b/plugins/lombok/lombok.k2/build.gradle.kts new file mode 100644 index 00000000000..78b1ea81edd --- /dev/null +++ b/plugins/lombok/lombok.k2/build.gradle.kts @@ -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() diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/FirLombokRegistrar.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/FirLombokRegistrar.kt new file mode 100644 index 00000000000..6d82014ae1b --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/FirLombokRegistrar.kt @@ -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 + } +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/LombokService.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/LombokService.kt new file mode 100644 index 00000000000..4b3b09a3983 --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/LombokService.kt @@ -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 = cachesFactory.createCache { symbol -> + Accessors.get(symbol.fir, config) + } + + private val accessorsIfAnnotatedCache: Cache = cachesFactory.createCache { symbol -> + Accessors.getIfAnnotated(symbol.fir, config) + } + + private val getterCache: Cache = cachesFactory.createCache { symbol -> + Getter.getOrNull(symbol.fir) + } + + private val setterCache: Cache = cachesFactory.createCache { symbol -> + Setter.getOrNull(symbol.fir) + } + + private val withCache: Cache = cachesFactory.createCache { symbol -> + With.getOrNull(symbol.fir) + } + + private val noArgsConstructorCache: Cache = cachesFactory.createCache { symbol -> + NoArgsConstructor.getOrNull(symbol.fir) + } + + private val allArgsConstructorCache: Cache = cachesFactory.createCache { symbol -> + AllArgsConstructor.getOrNull(symbol.fir) + } + + private val requiredArgsConstructorCache: Cache = cachesFactory.createCache { symbol -> + RequiredArgsConstructor.getOrNull(symbol.fir) + } + + private val dataCache: Cache = cachesFactory.createCache { symbol -> + Data.getOrNull(symbol.fir) + } + + private val valueCache: Cache = 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 = FirCache, T, Nothing?> + +val FirSession.lombokService: LombokService by FirSession.sessionComponentAccessor() diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/annotationConfig.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/annotationConfig.kt new file mode 100644 index 00000000000..ea2f656c3f4 --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/annotationConfig.kt @@ -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.findAnnotation(classId: ClassId): FirAnnotation? { + return firstOrNull { it.annotationTypeRef.coneTypeSafe()?.lookupTag?.classId == classId } +} + +abstract class ConeAnnotationCompanion(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(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 = emptyList() + ) { + companion object : ConeAnnotationAndConfigCompanion(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(LombokNames.GETTER_ID) { + override fun extract(annotation: FirAnnotation): Getter = Getter( + visibility = getAccessLevel(annotation) + ) + } + } + + class Setter(val visibility: AccessLevel = AccessLevel.PUBLIC) { + companion object : ConeAnnotationCompanion(LombokNames.SETTER_ID) { + override fun extract(annotation: FirAnnotation): Setter = Setter( + visibility = getAccessLevel(annotation) + ) + } + } + + class With(val visibility: AccessLevel = AccessLevel.PUBLIC) { + companion object : ConeAnnotationCompanion(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(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(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(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(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(LombokNames.VALUE_ID) { + override fun extract(annotation: FirAnnotation): Value = Value( + staticConstructor = annotation.getNonBlankStringArgument(STATIC_CONSTRUCTOR) + ) + } + } +} + + diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/annotationUtils.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/annotationUtils.kt new file mode 100644 index 00000000000..f09cded42f6 --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/config/annotationUtils.kt @@ -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" +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/AbstractConstructorGeneratorPart.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/AbstractConstructorGeneratorPart.kt new file mode 100644 index 00000000000..ea63d09aec0 --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/AbstractConstructorGeneratorPart.kt @@ -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(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 + + @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 = 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 + get() = original.annotations + override val isDeprecatedInJavaDoc: Boolean + get() = original.isDeprecatedInJavaDoc + + override fun findAnnotation(fqName: FqName): JavaAnnotation? { + return original.findAnnotation(fqName) + } + + override val upperBounds: Collection + get() = original.upperBounds +} + +private class JavaClassifierTypeStub( + val original: JavaClassifierType, + override val typeArguments: List, +) : JavaClassifierType { + override val annotations: Collection + 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 + get() = emptyList() + override val isDeprecatedInJavaDoc: Boolean + get() = false + override val typeArguments: List + 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) : 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) + } + } +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/AllArgsConstructorGeneratorPart.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/AllArgsConstructorGeneratorPart.kt new file mode 100644 index 00000000000..857b2fb909e --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/AllArgsConstructorGeneratorPart.kt @@ -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(session) { + override fun getConstructorInfo(classSymbol: FirClassSymbol<*>): AllArgsConstructor? { + return lombokService.getAllArgsConstructor(classSymbol) + ?: lombokService.getValue(classSymbol)?.asAllArgsConstructor() + } + + @OptIn(SymbolInternals::class) + override fun getFieldsForParameters(classSymbol: FirClassSymbol<*>): List { + return classSymbol.fir.declarations.filterIsInstance() + } +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/GetterGenerator.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/GetterGenerator.kt new file mode 100644 index 00000000000..92c3860cecf --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/GetterGenerator.kt @@ -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, Map?, Nothing?> = + session.firCachesFactory.createCache(::createGetters) + + override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set { + if (!classSymbol.isSuitableJavaClass()) return emptySet() + return cache.getValue(classSymbol)?.keys ?: emptySet() + } + + override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List { + 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? { + 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>? { + val classGetter = lombokService.getGetter(classSymbol) + ?: lombokService.getData(classSymbol)?.asGetter() + ?: lombokService.getValue(classSymbol)?.asGetter() + + return classSymbol.fir.declarations + .filterIsInstance() + .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) + } +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/LombokConstructorsGenerator.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/LombokConstructorsGenerator.kt new file mode 100644 index 00000000000..32bd301d1ed --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/LombokConstructorsGenerator.kt @@ -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> = listOf( + AllArgsConstructorGeneratorPart(session), + NoArgsConstructorGeneratorPart(session), + RequiredArgsConstructorGeneratorPart(session) + ) + + private val cache: FirCache, Collection>?, Nothing?> = + session.firCachesFactory.createCache(::createConstructors) + + override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set { + 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 { + val owner = context?.owner ?: return emptyList() + if (!owner.isSuitableJavaClass()) return emptyList() + return cache.getValue(owner)?.filterIsInstance().orEmpty() + } + + override fun generateConstructors(context: MemberGenerationContext): List { + val owner = context.owner + if (!owner.isSuitableJavaClass()) return emptyList() + return cache.getValue(owner)?.filterIsInstance().orEmpty() + } + + private fun createConstructors(classSymbol: FirClassSymbol<*>): Collection>? { + return parts + .mapNotNull { it.createConstructor(classSymbol) } + .takeIf { it.isNotEmpty() } + ?.filterClashingDeclarations(classSymbol) + } +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/NoArgsConstructorGeneratorPart.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/NoArgsConstructorGeneratorPart.kt new file mode 100644 index 00000000000..4afc537e342 --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/NoArgsConstructorGeneratorPart.kt @@ -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(session) { + override fun getConstructorInfo(classSymbol: FirClassSymbol<*>): NoArgsConstructor? { + return lombokService.getNoArgsConstructor(classSymbol) + } + + override fun getFieldsForParameters(classSymbol: FirClassSymbol<*>): List { + return emptyList() + } +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/RequiredArgsConstructorGeneratorPart.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/RequiredArgsConstructorGeneratorPart.kt new file mode 100644 index 00000000000..9a614920a62 --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/RequiredArgsConstructorGeneratorPart.kt @@ -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(session) { + override fun getConstructorInfo(classSymbol: FirClassSymbol<*>): RequiredArgsConstructor? { + return lombokService.getRequiredArgsConstructor(classSymbol) + ?: lombokService.getData(classSymbol)?.asRequiredArgsConstructor() + } + + @OptIn(SymbolInternals::class) + override fun getFieldsForParameters(classSymbol: FirClassSymbol<*>): List { + return classSymbol.fir.declarations + .filterIsInstance() + .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 } + } +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/SetterGenerator.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/SetterGenerator.kt new file mode 100644 index 00000000000..4858b526cbb --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/SetterGenerator.kt @@ -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, Map?, Nothing?> = + session.firCachesFactory.createCache(::createSetters) + + override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set { + if (!classSymbol.isSuitableForSetters()) return emptySet() + return cache.getValue(classSymbol)?.keys ?: emptySet() + } + + override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List { + 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? { + 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>? { + val classSetter = lombokService.getSetter(classSymbol) + ?: lombokService.getData(classSymbol)?.asSetter() + + return classSymbol.fir.declarations + .filterIsInstance() + .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) + } +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/WithGenerator.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/WithGenerator.kt new file mode 100644 index 00000000000..afe4e99f122 --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/WithGenerator.kt @@ -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, Map?, Nothing?> = + session.firCachesFactory.createCache(::createWith) + + override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set { + if (!classSymbol.isSuitableJavaClass()) return emptySet() + return cache.getValue(classSymbol)?.keys ?: emptySet() + } + + override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List { + 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? { + 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>? { + val classWith = lombokService.getWith(classSymbol) + + return classSymbol.fir.declarations + .filterIsInstance() + .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) + } +} diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/utils.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/utils.kt new file mode 100644 index 00000000000..bfc4e3044c7 --- /dev/null +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/generators/utils.kt @@ -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()?.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.filterClashingDeclarations(classSymbol: FirClassSymbol<*>): List> { + @Suppress("UNCHECKED_CAST") + val allStaticFunctionsAndConstructors = classSymbol.fir.declarations.filterIsInstance().toMutableList() + val result = mutableListOf() + 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 +} diff --git a/plugins/lombok/testData/box/data.kt b/plugins/lombok/testData/box/data.kt index a2677af77a5..a37b330eb38 100644 --- a/plugins/lombok/testData/box/data.kt +++ b/plugins/lombok/testData/box/data.kt @@ -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 { private final String name; private final T value; + private final List list; } public static void usage() { @@ -21,7 +26,7 @@ import lombok.*; obj.getTags(); obj.setScore(1.5); - Exercise ex = Exercise.of("name", 12); + Exercise 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 = DataExample.Exercise.of("name", 12) + val ex: DataExample.Exercise = DataExample.Exercise.of("name", 12, listOf(1, 2, 3)) return "OK" } diff --git a/plugins/lombok/testData/diagnostics/accessorsStripPrefixCombined.fir.kt b/plugins/lombok/testData/diagnostics/accessorsStripPrefixCombined.fir.kt new file mode 100644 index 00000000000..0b70ade3618 --- /dev/null +++ b/plugins/lombok/testData/diagnostics/accessorsStripPrefixCombined.fir.kt @@ -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(PrefixJava().propA, "A") + //not generated because doesn't have prefix from config + assertEquals(PrefixJava().propC, "C") + assertEquals(PrefixJava().propD, "D") +} + +// FILE: lombok.config +lombok.accessors.prefix += pxa diff --git a/plugins/lombok/testData/diagnostics/accessorsStripPrefixCombined.kt b/plugins/lombok/testData/diagnostics/accessorsStripPrefixCombined.kt index 62c49ca0e3e..8c8dc263f6c 100644 --- a/plugins/lombok/testData/diagnostics/accessorsStripPrefixCombined.kt +++ b/plugins/lombok/testData/diagnostics/accessorsStripPrefixCombined.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // KT-46529 // FILE: PrefixJava.java diff --git a/plugins/lombok/testData/diagnostics/getters.fir.kt b/plugins/lombok/testData/diagnostics/getters.fir.kt new file mode 100644 index 00000000000..2741b3972d8 --- /dev/null +++ b/plugins/lombok/testData/diagnostics/getters.fir.kt @@ -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.primitiveBoolean + obj.isPrimitiveBoolean() + + obj.boxedBoolean + obj.getBoxedBoolean() + + //shouldn't be accesible from here + obj.getName() + + obj.getInvisible() + + OverridenGetterTest().usage() +} + +class OverridenGetterTest : GetterTest() { + fun usage() { + getName() + } +} + diff --git a/plugins/lombok/testData/diagnostics/getters.kt b/plugins/lombok/testData/diagnostics/getters.kt index 850358ff74b..200e7761ea1 100644 --- a/plugins/lombok/testData/diagnostics/getters.kt +++ b/plugins/lombok/testData/diagnostics/getters.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FILE: GetterTest.java import lombok.AccessLevel; diff --git a/plugins/lombok/testData/diagnostics/gettersClassLevel.fir.kt b/plugins/lombok/testData/diagnostics/gettersClassLevel.fir.kt new file mode 100644 index 00000000000..abc3698574e --- /dev/null +++ b/plugins/lombok/testData/diagnostics/gettersClassLevel.fir.kt @@ -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.getName() + + OverridenGetterTest().usage() +} + +class OverridenGetterTest : ClassLevelGetterTest() { + fun usage() { + getName() + } +} diff --git a/plugins/lombok/testData/diagnostics/gettersClassLevel.kt b/plugins/lombok/testData/diagnostics/gettersClassLevel.kt index 3355b9832c1..97f6d2259d4 100644 --- a/plugins/lombok/testData/diagnostics/gettersClassLevel.kt +++ b/plugins/lombok/testData/diagnostics/gettersClassLevel.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FILE: ClassLevelGetterTest.java import lombok.AccessLevel; diff --git a/plugins/lombok/testData/diagnostics/setters.fir.kt b/plugins/lombok/testData/diagnostics/setters.fir.kt new file mode 100644 index 00000000000..71bc58bc43f --- /dev/null +++ b/plugins/lombok/testData/diagnostics/setters.fir.kt @@ -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.primitiveBoolean = false + obj.setPrimitiveBoolean(true) + + //shouldn't be accesible from here + obj.setName("abc") + + OverridenGetterTest().usage() +} + +class OverridenGetterTest : SetterTest() { + fun usage() { + setName("abc") + } +} diff --git a/plugins/lombok/testData/diagnostics/setters.kt b/plugins/lombok/testData/diagnostics/setters.kt index 692550c49e5..7eea918851b 100644 --- a/plugins/lombok/testData/diagnostics/setters.kt +++ b/plugins/lombok/testData/diagnostics/setters.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FILE: SetterTest.java import lombok.AccessLevel; diff --git a/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/DiagnosticTestForLombokGenerated.java b/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/DiagnosticTestForLombokGenerated.java index 28dd518ab59..ec210b76f84 100644 --- a/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/DiagnosticTestForLombokGenerated.java +++ b/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/DiagnosticTestForLombokGenerated.java @@ -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 diff --git a/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/FirBlackBoxCodegenTestForLombokGenerated.java b/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/FirBlackBoxCodegenTestForLombokGenerated.java new file mode 100644 index 00000000000..83b6af9a5b1 --- /dev/null +++ b/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/FirBlackBoxCodegenTestForLombokGenerated.java @@ -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"); + } +} diff --git a/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/FirDiagnosticTestForLombokGenerated.java b/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/FirDiagnosticTestForLombokGenerated.java new file mode 100644 index 00000000000..ce0be8a774a --- /dev/null +++ b/plugins/lombok/tests-gen/org/jetbrains/kotlin/lombok/FirDiagnosticTestForLombokGenerated.java @@ -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"); + } +} diff --git a/plugins/lombok/tests/org/jetbrains/kotlin/lombok/LombokTests.kt b/plugins/lombok/tests/org/jetbrains/kotlin/lombok/LombokTests.kt index 4214d34f883..3bf78ecb460 100644 --- a/plugins/lombok/tests/org/jetbrains/kotlin/lombok/LombokTests.kt +++ b/plugins/lombok/tests/org/jetbrains/kotlin/lombok/LombokTests.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() { diff --git a/settings.gradle b/settings.gradle index d6e3c183c70..72d783654d6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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