[lombok] Experimental lombok plugin prototype
Add extension point for java descriptors Add simple usage of this point to generate getter method Add simple test infrastructure to test compilation with lombok plugin
This commit is contained in:
committed by
TeamCityServer
parent
ac86ad252f
commit
f71e08df4d
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+32
@@ -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<SyntheticJavaResolveExtension>(
|
||||
"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
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+3
@@ -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 {
|
||||
|
||||
+37
@@ -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<Name>
|
||||
|
||||
fun generateSyntheticMethods(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
class CompositeSyntheticJavaPartsProvider(private val inner: List<SyntheticJavaPartsProvider>) : SyntheticJavaPartsProvider {
|
||||
override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> =
|
||||
inner.flatMap { it.getSyntheticFunctionNames(thisDescriptor) }
|
||||
|
||||
override fun generateSyntheticMethods(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) = inner.forEach { it.generateSyntheticMethods(thisDescriptor, name, result) }
|
||||
}
|
||||
@@ -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"))
|
||||
|
||||
@@ -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<String>) {
|
||||
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<AbstractLombokCompileTest> {
|
||||
model("compile")
|
||||
}
|
||||
}
|
||||
/*
|
||||
testGroup("plugins/android-extensions/android-extensions-idea/tests", "plugins/android-extensions/android-extensions-idea/testData") {
|
||||
testClass<AbstractAndroidCompletionTest> {
|
||||
|
||||
@@ -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<String>) {
|
||||
model("quickfix", pattern = "^([\\w\\-_]+)\\.kt$", filenameStartsLowerCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("plugins/lombok/lombok-compiler-plugin/tests", "plugins/lombok/lombok-compiler-plugin/testData") {
|
||||
testClass<AbstractLombokCompileTest> {
|
||||
model("compile")
|
||||
}
|
||||
}
|
||||
/*
|
||||
testGroup("plugins/android-extensions/android-extensions-idea/tests", "plugins/android-extensions/android-extensions-idea/testData") {
|
||||
testClass<AbstractAndroidCompletionTest> {
|
||||
|
||||
@@ -11054,6 +11054,12 @@
|
||||
<sha256 value="e07bce4bb55d5a06f4c10d912fc9dee8a9b9c04ec549bbb8db4f20db34706f75" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.projectlombok" name="lombok" version="1.18.16">
|
||||
<artifact name="lombok-1.18.16.jar">
|
||||
<md5 value="fca4ae86d65bc29ad22cb971bd3e3700"/>
|
||||
<sha256 value="7206cbbfd6efd5e85bceff29545633645650be58d58910a23b0d4835fbd15ed7"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.robolectric" name="android-all" version="4.4_r1-robolectric-1">
|
||||
<artifact name="android-all-4.4_r1-robolectric-1.jar">
|
||||
<md5 value="1e478f10e107d1dddad09d32508f2ec9" origin="Generated by Gradle"/>
|
||||
|
||||
@@ -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()
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.lombok.LombokComponentRegistrar
|
||||
+25
@@ -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)
|
||||
}
|
||||
}
|
||||
+14
@@ -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()
|
||||
}
|
||||
+57
@@ -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<Processor> =
|
||||
listOf(
|
||||
GetterProcessor()
|
||||
)
|
||||
|
||||
private val partsCache: MutableMap<ClassDescriptor, Parts> = WeakHashMap()
|
||||
|
||||
override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> =
|
||||
getSyntheticParts(thisDescriptor).methods.map { it.name }
|
||||
|
||||
override fun generateSyntheticMethods(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) {
|
||||
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 }
|
||||
|
||||
|
||||
}
|
||||
+9
@@ -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 {
|
||||
}
|
||||
+63
@@ -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 <E, R> Collection<E>.collectWithNotNull(f: (E) -> R?): List<Pair<E, R>> =
|
||||
map { it to f(it) }.filter { it.second != null } as List<Pair<E, R>>
|
||||
|
||||
private fun KotlinType.isPrimitiveBoolean(): Boolean = this is SimpleTypeMarker && isBoolean() //todo
|
||||
|
||||
}
|
||||
+17
@@ -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<SimpleFunctionDescriptor> = emptyList()) {
|
||||
|
||||
operator fun plus(other: Parts): Parts = Parts(methods + other.methods)
|
||||
|
||||
companion object {
|
||||
val Empty = Parts()
|
||||
}
|
||||
}
|
||||
+14
@@ -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
|
||||
}
|
||||
+15
@@ -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")
|
||||
|
||||
}
|
||||
+63
@@ -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")
|
||||
}
|
||||
}
|
||||
+39
@@ -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<ValueParameterDescriptor>,
|
||||
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
|
||||
}
|
||||
+10
@@ -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()
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+31
@@ -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<TestFile>) {
|
||||
compile(files)
|
||||
}
|
||||
|
||||
override fun setupEnvironment(environment: KotlinCoreEnvironment) {
|
||||
LombokComponentRegistrar.registerComponents(environment.project)
|
||||
environment.updateClasspath(listOf(JvmClasspathRoot(getLombokJar())))
|
||||
}
|
||||
|
||||
override fun updateJavaClasspath(javaClasspath: MutableList<String>) {
|
||||
javaClasspath += getLombokJar().absolutePath
|
||||
}
|
||||
|
||||
private fun getLombokJar(): File = PathUtil.getResourcePathForClass(Getter::class.java)
|
||||
}
|
||||
+41
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user