diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index d3f0098510f..ea121e8f10f 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -33,13 +33,11 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.extensions.ExtensionsArea import com.intellij.openapi.fileTypes.PlainTextFileType import com.intellij.openapi.project.Project -import com.intellij.openapi.roots.LanguageLevelProjectExtension import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.io.FileUtilRt import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.* import com.intellij.openapi.vfs.impl.ZipHandler -import com.intellij.pom.java.LanguageLevel import com.intellij.psi.PsiElementFinder import com.intellij.psi.PsiManager import com.intellij.psi.impl.JavaClassSupersImpl @@ -94,6 +92,7 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension +import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension @@ -577,6 +576,7 @@ class KotlinCoreEnvironment private constructor( fun registerPluginExtensionPoints(project: MockProject) { ExpressionCodegenExtension.registerExtensionPoint(project) SyntheticResolveExtension.registerExtensionPoint(project) + SyntheticJavaResolveExtension.registerExtensionPoint(project) ClassBuilderInterceptorExtension.registerExtensionPoint(project) AnalysisHandlerExtension.registerExtensionPoint(project) PackageFragmentProviderExtension.registerExtensionPoint(project) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.as42 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.as42 index 06c1336875b..698c016c0f0 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.as42 +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.as42 @@ -96,6 +96,7 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension +import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension @@ -569,6 +570,7 @@ class KotlinCoreEnvironment private constructor( fun registerPluginExtensionPoints(project: MockProject) { ExpressionCodegenExtension.registerExtensionPoint(project) SyntheticResolveExtension.registerExtensionPoint(project) + SyntheticJavaResolveExtension.registerExtensionPoint(project) ClassBuilderInterceptorExtension.registerExtensionPoint(project) AnalysisHandlerExtension.registerExtensionPoint(project) PackageFragmentProviderExtension.registerExtensionPoint(project) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt index de33aa3c096..5098a254c60 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.calls.tower.ImplicitsExtensionsResolutionFilter import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.JvmDiagnosticComponents +import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension import org.jetbrains.kotlin.resolve.jvm.multiplatform.OptionalAnnotationPackageFragmentProvider import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory @@ -116,6 +117,8 @@ fun StorageComponentContainer.configureJavaSpecificComponents( useInstance(JavaDeprecationSettings) useInstance(moduleClassResolver) + useInstance(SyntheticJavaResolveExtension.getProvider(moduleContext.project)) + if (configureJavaClassFinder != null) { configureJavaClassFinder() } else { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/SyntheticJavaResolveExtension.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/SyntheticJavaResolveExtension.kt new file mode 100644 index 00000000000..a9b3dbbfa4e --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/SyntheticJavaResolveExtension.kt @@ -0,0 +1,32 @@ +/* + * 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.resolve.jvm.extensions + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor +import org.jetbrains.kotlin.resolve.jvm.CompositeSyntheticJavaPartsProvider +import org.jetbrains.kotlin.resolve.jvm.SyntheticJavaPartsProvider + +interface SyntheticJavaResolveExtension { + + companion object : ProjectExtensionDescriptor( + "org.jetbrains.kotlin.syntheticJavaResolveExtension", SyntheticJavaResolveExtension::class.java + ) { + fun getProvider(project: Project): SyntheticJavaPartsProvider { + val instances = getInstances(project) + val providers = instances.map { it.getProvider() } + return if (providers.isEmpty()) { + SyntheticJavaPartsProvider.EMPTY + } else { + CompositeSyntheticJavaPartsProvider(providers) + } + } + } + + fun getProvider(): SyntheticJavaPartsProvider + +} + diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt index fcf8a04dde1..04a26f13c21 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.resolve.jvm.SyntheticJavaPartsProvider import org.jetbrains.kotlin.resolve.sam.SamConversionResolver import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter import org.jetbrains.kotlin.storage.StorageManager @@ -63,7 +64,8 @@ class JavaResolverComponents( val javaClassesTracker: JavaClassesTracker, val settings: JavaResolverSettings, val kotlinTypeChecker: NewKotlinTypeChecker, - val javaTypeEnhancementState: JavaTypeEnhancementState + val javaTypeEnhancementState: JavaTypeEnhancementState, + val syntheticPartsProvider: SyntheticJavaPartsProvider = SyntheticJavaPartsProvider.EMPTY ) { fun replace( javaResolverCache: JavaResolverCache = this.javaResolverCache diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt index c19ec5ba853..446ab33119d 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt @@ -78,6 +78,7 @@ class LazyJavaClassMemberScope( addAll(declaredMemberIndex().getMethodNames()) addAll(declaredMemberIndex().getRecordComponentNames()) addAll(computeClassNames(kindFilter, nameFilter)) + addAll(c.components.syntheticPartsProvider.getSyntheticFunctionNames(ownerDescriptor)) } internal val constructors = c.storageManager.createLazyValue { @@ -496,6 +497,8 @@ class LazyJavaClassMemberScope( if (jClass.isRecord && declaredMemberIndex().findRecordComponentByName(name) != null && result.none { it.valueParameters.isEmpty() }) { result.add(resolveRecordComponentToFunctionDescriptor(declaredMemberIndex().findRecordComponentByName(name)!!)) } + + c.components.syntheticPartsProvider.generateSyntheticMethods(ownerDescriptor, name, result) } private fun resolveRecordComponentToFunctionDescriptor(recordComponent: JavaRecordComponent): JavaMethodDescriptor { diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/resolve/jvm/SyntheticJavaPartsProvider.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/resolve/jvm/SyntheticJavaPartsProvider.kt new file mode 100644 index 00000000000..28088a61914 --- /dev/null +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/resolve/jvm/SyntheticJavaPartsProvider.kt @@ -0,0 +1,37 @@ +/* + * 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.resolve.jvm + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.name.Name + +interface SyntheticJavaPartsProvider { + + companion object { + val EMPTY = CompositeSyntheticJavaPartsProvider(emptyList()) + } + + fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List + + fun generateSyntheticMethods( + thisDescriptor: ClassDescriptor, + name: Name, + result: MutableCollection + ) + +} + +class CompositeSyntheticJavaPartsProvider(private val inner: List) : SyntheticJavaPartsProvider { + override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List = + inner.flatMap { it.getSyntheticFunctionNames(thisDescriptor) } + + override fun generateSyntheticMethods( + thisDescriptor: ClassDescriptor, + name: Name, + result: MutableCollection + ) = inner.forEach { it.generateSyntheticMethods(thisDescriptor, name, result) } +} diff --git a/generators/build.gradle.kts b/generators/build.gradle.kts index 1c7578b493b..d25a8369aed 100644 --- a/generators/build.gradle.kts +++ b/generators/build.gradle.kts @@ -68,6 +68,7 @@ dependencies { testCompile(projectTests(":kotlin-annotation-processing-cli")) testCompile(projectTests(":kotlin-allopen-compiler-plugin")) testCompile(projectTests(":kotlin-noarg-compiler-plugin")) + testCompile(projectTests(":plugins:lombok:lombok-compiler-plugin")) testCompile(projectTests(":kotlin-sam-with-receiver-compiler-plugin")) testCompile(projectTests(":kotlinx-serialization-compiler-plugin")) testCompile(projectTests(":kotlinx-serialization-ide-plugin")) diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index c6bb76dc039..6d81d976cb0 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -166,6 +166,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.AbstractLombokCompileTest import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterMultiFileTest import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest @@ -1858,6 +1859,12 @@ fun main(args: Array) { model("handlers/charFilter", testMethod = "doPerfTest", pattern = KT_WITHOUT_DOTS_IN_NAME) } } + + testGroup("plugins/lombok/lombok-compiler-plugin/tests", "plugins/lombok/lombok-compiler-plugin/testData") { + testClass { + model("compile") + } + } /* testGroup("plugins/android-extensions/android-extensions-idea/tests", "plugins/android-extensions/android-extensions-idea/testData") { testClass { diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41 b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as42 b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as42 index ad38f939289..f7df952dd15 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as42 +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as42 @@ -137,6 +137,7 @@ import org.jetbrains.kotlin.kapt.cli.test.AbstractArgumentParsingTest import org.jetbrains.kotlin.kapt.cli.test.AbstractKaptToolIntegrationTest import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTest import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest +import org.jetbrains.kotlin.lombok.AbstractLombokCompileTest import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterMultiFileTest import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinConverterSingleFileTest import org.jetbrains.kotlin.nj2k.AbstractNewJavaToKotlinCopyPasteConversionTest @@ -1140,6 +1141,12 @@ fun main(args: Array) { model("quickfix", pattern = "^([\\w\\-_]+)\\.kt$", filenameStartsLowerCase = true) } } + + testGroup("plugins/lombok/lombok-compiler-plugin/tests", "plugins/lombok/lombok-compiler-plugin/testData") { + testClass { + model("compile") + } + } /* testGroup("plugins/android-extensions/android-extensions-idea/tests", "plugins/android-extensions/android-extensions-idea/testData") { testClass { diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 7cae5969972..e58cdcb3474 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -11054,6 +11054,12 @@ + + + + + + diff --git a/plugins/lombok/lombok-compiler-plugin/build.gradle.kts b/plugins/lombok/lombok-compiler-plugin/build.gradle.kts new file mode 100644 index 00000000000..3b1a09aace3 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/build.gradle.kts @@ -0,0 +1,42 @@ +description = "Lombok compiler plugin" + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + implementation(project(":compiler:util")) + implementation(project(":compiler:plugin-api")) + implementation(project(":compiler:frontend")) + implementation(project(":compiler:frontend.java")) + + compileOnly(intellijCoreDep()) { includeJars("intellij-core") } + + + testImplementation(intellijCoreDep()) { includeJars("intellij-core") } + testImplementation(commonDep("junit:junit")) + testImplementation(projectTests(":compiler:tests-common")) + + testImplementation("org.projectlombok:lombok:1.18.16") + + testRuntimeOnly(toolsJar()) +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +projectTest(parallel = true) { + workingDir = rootDir + dependsOn(":dist") +} + +publish() + +runtimeJar() +testsJar() + +sourcesJar() +javadocJar() diff --git a/plugins/lombok/lombok-compiler-plugin/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar b/plugins/lombok/lombok-compiler-plugin/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar new file mode 100644 index 00000000000..b9bd03db91b --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar @@ -0,0 +1 @@ +org.jetbrains.kotlin.lombok.LombokComponentRegistrar diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/LombokComponentRegistrar.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/LombokComponentRegistrar.kt new file mode 100644 index 00000000000..be8920961cd --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/LombokComponentRegistrar.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 + +import com.intellij.mock.MockProject +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension + +class LombokComponentRegistrar : ComponentRegistrar { + + companion object { + fun registerComponents(project: Project) { + SyntheticJavaResolveExtension.registerExtension(project, LombokResolveExtension()) + } + } + + override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { + registerComponents(project) + } +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/LombokResolveExtension.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/LombokResolveExtension.kt new file mode 100644 index 00000000000..157956f8354 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/LombokResolveExtension.kt @@ -0,0 +1,14 @@ +/* + * 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 org.jetbrains.kotlin.resolve.jvm.SyntheticJavaPartsProvider +import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension + +class LombokResolveExtension : SyntheticJavaResolveExtension { + + override fun getProvider(): SyntheticJavaPartsProvider = LombokSyntheticJavaPartsProvider() +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/LombokSyntheticJavaPartsProvider.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/LombokSyntheticJavaPartsProvider.kt new file mode 100644 index 00000000000..e4947eb5429 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/LombokSyntheticJavaPartsProvider.kt @@ -0,0 +1,57 @@ +/* + * 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 org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor +import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl +import org.jetbrains.kotlin.lombok.processor.GetterProcessor +import org.jetbrains.kotlin.lombok.processor.Parts +import org.jetbrains.kotlin.lombok.processor.Processor +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.jvm.SyntheticJavaPartsProvider +import java.util.* + +class LombokSyntheticJavaPartsProvider : SyntheticJavaPartsProvider { + + private val processors = initProcessors() + + private fun initProcessors(): List = + listOf( + GetterProcessor() + ) + + private val partsCache: MutableMap = WeakHashMap() + + override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List = + getSyntheticParts(thisDescriptor).methods.map { it.name } + + override fun generateSyntheticMethods( + thisDescriptor: ClassDescriptor, + name: Name, + result: MutableCollection + ) { + getSyntheticParts(thisDescriptor).methods.find { it.name == name }?.let { + result.add(it) + } + } + + private fun extractClass(descriptor: ClassDescriptor): JavaClassImpl? = + (descriptor as? LazyJavaClassDescriptor)?.jClass as? JavaClassImpl + + private fun getSyntheticParts(descriptor: ClassDescriptor): Parts = + extractClass(descriptor)?.let { jClass -> + partsCache.getOrPut(descriptor) { + computeSyntheticParts(descriptor, jClass) + } + } ?: Parts.Empty + + private fun computeSyntheticParts(descriptor: ClassDescriptor, jClass: JavaClassImpl): Parts = + processors.map { it.contribute(descriptor, jClass) }.reduce { a, b -> a + b } + + +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/config/LombokConfig.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/config/LombokConfig.kt new file mode 100644 index 00000000000..2fd9740dbe1 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/config/LombokConfig.kt @@ -0,0 +1,9 @@ +/* + * 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.config + +class LombokConfig { +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/GetterProcessor.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/GetterProcessor.kt new file mode 100644 index 00000000000..73d056ffc94 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/GetterProcessor.kt @@ -0,0 +1,63 @@ +/* + * 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.processor + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl +import org.jetbrains.kotlin.lombok.utils.LombokAnnotationNames +import org.jetbrains.kotlin.lombok.utils.createFunction +import org.jetbrains.kotlin.lombok.utils.getVisibility +import org.jetbrains.kotlin.lombok.utils.toPreparedBase +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.model.SimpleTypeMarker +import org.jetbrains.kotlin.types.typeUtil.isBoolean +import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType + +class GetterProcessor : Processor { + + override fun contribute(classDescriptor: ClassDescriptor, jClass: JavaClassImpl): Parts { + + val functions = classDescriptor.unsubstitutedMemberScope.getVariableNames() + .map { + classDescriptor.unsubstitutedMemberScope.getContributedVariables(it, NoLookupLocation.FROM_SYNTHETIC_SCOPE).single() + }.collectWithNotNull { it.annotations.findAnnotation(LombokAnnotationNames.GETTER) } + .mapNotNull { (field, annotation) -> createGetter(classDescriptor, field, annotation) } + +// val functions = jClass.fields.collectWithNotNull { it.findAnnotation(LombokAnnotationNames.GETTER) }.mapNotNull { (field, annotation) -> +// createGetter(classDescriptor, field, annotation) +// } + return Parts(functions) + } + + private fun createGetter( + classDescriptor: ClassDescriptor, + field: PropertyDescriptor, + annotation: AnnotationDescriptor + ): SimpleFunctionDescriptor? { + val prefix = if (field.type.isPrimitiveBoolean()) "is" else "get" + val functionName = Name.identifier(prefix + toPreparedBase(field.name.identifier)) + return createFunction( + classDescriptor, + functionName, + emptyList(), + field.returnType, + visibility = getVisibility(annotation) + ) + } + + @Suppress("UNCHECKED_CAST") + internal fun Collection.collectWithNotNull(f: (E) -> R?): List> = + map { it to f(it) }.filter { it.second != null } as List> + + private fun KotlinType.isPrimitiveBoolean(): Boolean = this is SimpleTypeMarker && isBoolean() //todo + +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/Parts.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/Parts.kt new file mode 100644 index 00000000000..73ca0b50fcb --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/Parts.kt @@ -0,0 +1,17 @@ +/* + * 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.processor + +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor + +data class Parts(val methods: List = emptyList()) { + + operator fun plus(other: Parts): Parts = Parts(methods + other.methods) + + companion object { + val Empty = Parts() + } +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/Processor.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/Processor.kt new file mode 100644 index 00000000000..01990dfe94a --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/Processor.kt @@ -0,0 +1,14 @@ +/* + * 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.processor + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl + +interface Processor { + + fun contribute(classDescriptor: ClassDescriptor, jClass: JavaClassImpl): Parts +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/LombokAnnotationNames.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/LombokAnnotationNames.kt new file mode 100644 index 00000000000..e2909dca267 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/LombokAnnotationNames.kt @@ -0,0 +1,15 @@ +/* + * 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.name.FqName + +object LombokAnnotationNames { + + val GETTER = FqName("lombok.Getter") + val SETTER = FqName("lombok.Setter") + +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/annotationUtils.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/annotationUtils.kt new file mode 100644 index 00000000000..1c4ed8ce428 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/annotationUtils.kt @@ -0,0 +1,63 @@ +/* + * 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.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.DescriptorVisibility +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.java.JavaVisibilities +import org.jetbrains.kotlin.load.java.structure.JavaAnnotation +import org.jetbrains.kotlin.load.java.structure.JavaEnumValueAnnotationArgument +import org.jetbrains.kotlin.load.java.structure.JavaLiteralAnnotationArgument +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.constants.EnumValue +import java.lang.IllegalArgumentException + +internal fun getVisibility(annotation: JavaAnnotation, field: String = "value"): DescriptorVisibility { + val value = getStringAnnotationValue(annotation, field, "PUBLIC") + val visibility = when (value) { + "PUBLIC" -> Visibilities.Public + "PROTECTED" -> Visibilities.Protected + "PRIVATE" -> Visibilities.Private + "PACKAGE" -> JavaVisibilities.PackageVisibility + else -> Visibilities.Public + } + return DescriptorVisibilities.toDescriptorVisibility(visibility) +} + +internal fun getVisibility(annotation: AnnotationDescriptor, field: String = "value"): DescriptorVisibility { + val value = getStringAnnotationValue(annotation, field, "PUBLIC") + val visibility = when (value) { + "PUBLIC" -> Visibilities.Public + "PROTECTED" -> Visibilities.Protected + "PRIVATE" -> Visibilities.Private + "PACKAGE" -> JavaVisibilities.PackageVisibility + else -> Visibilities.Public + } + return DescriptorVisibilities.toDescriptorVisibility(visibility) +} + +private fun getStringAnnotationValue(annotation: JavaAnnotation, argumentName: String, default: String): String { + val argument = annotation.arguments.find { it.name?.identifier == argumentName } + ?: throw IllegalArgumentException("No argument '$argumentName' found in $annotation") + + return when (argument) { + is JavaEnumValueAnnotationArgument -> argument.entryName?.asString() ?: default + is JavaLiteralAnnotationArgument -> argument.value?.toString() ?: default + else -> throw RuntimeException("Argument $argument is not supported") + } +} + +private fun getStringAnnotationValue(annotation: AnnotationDescriptor, argumentName: String, default: String): String { + val argument = annotation.allValueArguments[Name.identifier(argumentName)] + ?: return default + + return when (argument) { + is EnumValue -> argument.enumEntryName.identifier + else -> throw RuntimeException("Argument $argument is not supported") + } +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/descriptorUtils.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/descriptorUtils.kt new file mode 100644 index 00000000000..780265e8e2d --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/descriptorUtils.kt @@ -0,0 +1,39 @@ +/* + * 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.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType + +internal fun createFunction( + containingClass: ClassDescriptor, + name: Name, + valueParameters: List, + returnType: KotlinType?, + modality: Modality? = Modality.OPEN, + visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC +): SimpleFunctionDescriptor { + val methodDescriptor = SimpleFunctionDescriptorImpl.create( + containingClass, + Annotations.EMPTY, + name, + CallableMemberDescriptor.Kind.SYNTHESIZED, + containingClass.source + ) + methodDescriptor.initialize( + null, + containingClass.thisAsReceiverParameter, + mutableListOf(), + valueParameters, + returnType, + modality, + visibility + ) + return methodDescriptor +} diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt new file mode 100644 index 00000000000..de816afd75e --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt @@ -0,0 +1,10 @@ +/* + * 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.util.capitalizeDecapitalize.capitalizeAsciiOnly + +internal fun toPreparedBase(name: String): String = name.capitalizeAsciiOnly() diff --git a/plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt b/plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt new file mode 100644 index 00000000000..ce67f112030 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt @@ -0,0 +1,38 @@ +//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; + + void test() { + getAge(); + isPrimitiveBoolean(); + } + +} + + +//FILE: test.kt + +object Test { + fun usage() { + val obj = GetterTest() + val getter = obj.getAge() + val property = obj.age + + //todo kotlin doesn't see isBoolean methods as properties +// obj.primitiveBoolean + obj.isPrimitiveBoolean() + + obj.boxedBoolean + obj.getBoxedBoolean() + } +} diff --git a/plugins/lombok/lombok-compiler-plugin/testData/compile/simple.kt b/plugins/lombok/lombok-compiler-plugin/testData/compile/simple.kt new file mode 100644 index 00000000000..0c180fdc438 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/testData/compile/simple.kt @@ -0,0 +1,22 @@ +//FILE: GetterSetterExample.java + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +public class GetterSetterExample { + @Getter @Setter private int age = 10; + + @Getter(AccessLevel.PROTECTED) private String name; +} + + +//FILE: test.kt + +object Test { + fun usage() { + val obj = GetterSetterExample() + val getter = obj.getAge() + val property = obj.age + } +} diff --git a/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/AbstractLombokCompileTest.kt b/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/AbstractLombokCompileTest.kt new file mode 100644 index 00000000000..085a91b2794 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/AbstractLombokCompileTest.kt @@ -0,0 +1,31 @@ +/* + * 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 lombok.Getter +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot +import org.jetbrains.kotlin.codegen.CodegenTestCase +import org.jetbrains.kotlin.utils.PathUtil +import java.io.File + +abstract class AbstractLombokCompileTest : CodegenTestCase() { + + override fun doMultiFileTest(wholeFile: File, files: List) { + compile(files) + } + + override fun setupEnvironment(environment: KotlinCoreEnvironment) { + LombokComponentRegistrar.registerComponents(environment.project) + environment.updateClasspath(listOf(JvmClasspathRoot(getLombokJar()))) + } + + override fun updateJavaClasspath(javaClasspath: MutableList) { + javaClasspath += getLombokJar().absolutePath + } + + private fun getLombokJar(): File = PathUtil.getResourcePathForClass(Getter::class.java) +} diff --git a/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/LombokCompileTestGenerated.java b/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/LombokCompileTestGenerated.java new file mode 100644 index 00000000000..6ac151fa190 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/LombokCompileTestGenerated.java @@ -0,0 +1,41 @@ +/* + * 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.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/lombok/lombok-compiler-plugin/testData/compile") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class LombokCompileTestGenerated extends AbstractLombokCompileTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCompile() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/lombok/lombok-compiler-plugin/testData/compile"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @TestMetadata("getters.kt") + public void testGetters() throws Exception { + runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/simple.kt"); + } +} diff --git a/settings.gradle b/settings.gradle index 692be47d2bb..a4ac5adffc9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -346,6 +346,8 @@ include ":plugins:parcelize:parcelize-compiler", ":plugins:parcelize:parcelize-runtime", ":kotlin-parcelize-compiler" +include ":plugins:lombok:lombok-compiler-plugin" + include ":prepare:ide-plugin-dependencies:android-extensions-compiler-plugin-for-ide", ":prepare:ide-plugin-dependencies:allopen-compiler-plugin-for-ide", ":prepare:ide-plugin-dependencies:allopen-compiler-plugin-tests-for-ide",