[lombok] @Setter support
This commit is contained in:
committed by
TeamCityServer
parent
70e3877efc
commit
7fd8f7b3bc
+5
-4
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.lombok.config.LombokConfig
|
||||
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.lombok.processor.SetterProcessor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.SyntheticJavaPartsProvider
|
||||
import java.util.*
|
||||
@@ -23,7 +24,8 @@ class LombokSyntheticJavaPartsProvider(private val config: LombokConfig) : Synth
|
||||
|
||||
private fun initProcessors(): List<Processor> =
|
||||
listOf(
|
||||
GetterProcessor(config)
|
||||
GetterProcessor(config),
|
||||
SetterProcessor(config)
|
||||
)
|
||||
|
||||
private val partsCache: MutableMap<ClassDescriptor, Parts> = WeakHashMap()
|
||||
@@ -36,9 +38,8 @@ class LombokSyntheticJavaPartsProvider(private val config: LombokConfig) : Synth
|
||||
name: Name,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) {
|
||||
getSyntheticParts(thisDescriptor).methods.find { it.name == name }?.let {
|
||||
result.add(it)
|
||||
}
|
||||
val methods = getSyntheticParts(thisDescriptor).methods.filter { it.name == name }
|
||||
result.addAll(methods)
|
||||
}
|
||||
|
||||
private fun extractClass(descriptor: ClassDescriptor): JavaClassImpl? =
|
||||
|
||||
+11
@@ -48,3 +48,14 @@ data class Getter(val visibility: DescriptorVisibility) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class Setter(val visibility: DescriptorVisibility) {
|
||||
companion object : AnnotationCompanion<Setter>() {
|
||||
override val name: FqName = LombokNames.SETTER
|
||||
|
||||
override fun extract(annotation: AnnotationDescriptor): Setter =
|
||||
Setter(
|
||||
visibility = getVisibility(annotation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-11
@@ -8,12 +8,13 @@ package org.jetbrains.kotlin.lombok.processor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
|
||||
import org.jetbrains.kotlin.lombok.config.Accessors
|
||||
import org.jetbrains.kotlin.lombok.config.Getter
|
||||
import org.jetbrains.kotlin.lombok.config.LombokConfig
|
||||
import org.jetbrains.kotlin.lombok.utils.collectWithNotNull
|
||||
import org.jetbrains.kotlin.lombok.utils.createFunction
|
||||
import org.jetbrains.kotlin.lombok.utils.getVariables
|
||||
import org.jetbrains.kotlin.lombok.utils.toPreparedBase
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -28,10 +29,9 @@ class GetterProcessor(private val config: LombokConfig) : Processor {
|
||||
val clAccessors = Accessors.getOrNull(classDescriptor)
|
||||
val clGetter = Getter.getOrNull(classDescriptor)
|
||||
|
||||
val functions = classDescriptor.unsubstitutedMemberScope.getVariableNames()
|
||||
.map {
|
||||
classDescriptor.unsubstitutedMemberScope.getContributedVariables(it, NoLookupLocation.FROM_SYNTHETIC_SCOPE).single()
|
||||
}.collectWithNotNull { Getter.getOrNull(it) ?: clGetter }
|
||||
val functions = classDescriptor
|
||||
.getVariables()
|
||||
.collectWithNotNull { Getter.getOrNull(it) ?: clGetter }
|
||||
.mapNotNull { (field, annotation) -> createGetter(classDescriptor, field, annotation, clAccessors) }
|
||||
|
||||
return Parts(functions)
|
||||
@@ -52,8 +52,7 @@ class GetterProcessor(private val config: LombokConfig) : Processor {
|
||||
val prefix = if (field.type.isPrimitiveBoolean() && !noIsPrefix) "is" else "get"
|
||||
prefix + toPreparedBase(field.name.identifier)
|
||||
}
|
||||
return createFunction(
|
||||
classDescriptor,
|
||||
return classDescriptor.createFunction(
|
||||
Name.identifier(functionName),
|
||||
emptyList(),
|
||||
field.returnType,
|
||||
@@ -61,10 +60,6 @@ class GetterProcessor(private val config: LombokConfig) : Processor {
|
||||
)
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
|
||||
import org.jetbrains.kotlin.lombok.config.Accessors
|
||||
import org.jetbrains.kotlin.lombok.config.LombokConfig
|
||||
import org.jetbrains.kotlin.lombok.config.Setter
|
||||
import org.jetbrains.kotlin.lombok.utils.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
|
||||
class SetterProcessor(private val config: LombokConfig) : Processor {
|
||||
|
||||
override fun contribute(classDescriptor: ClassDescriptor, jClass: JavaClassImpl): Parts {
|
||||
val clAccessors = Accessors.getOrNull(classDescriptor)
|
||||
val clSetter = Setter.getOrNull(classDescriptor)
|
||||
|
||||
val functions = classDescriptor
|
||||
.getVariables()
|
||||
.collectWithNotNull { field -> Setter.getOrNull(field) ?: clSetter.takeIf { field.isVar } }
|
||||
.mapNotNull { (field, setter) -> createSetter(classDescriptor, field, setter, clAccessors) }
|
||||
return Parts(functions)
|
||||
}
|
||||
|
||||
private fun createSetter(
|
||||
classDescriptor: ClassDescriptor,
|
||||
field: PropertyDescriptor,
|
||||
getter: Setter,
|
||||
classLevelAccessors: Accessors?
|
||||
): SimpleFunctionDescriptor? {
|
||||
val accessors = Accessors.getOrNull(field) ?: classLevelAccessors ?: Accessors.default
|
||||
|
||||
val functionName =
|
||||
if (accessors.fluent) {
|
||||
field.name.identifier
|
||||
} else {
|
||||
"set" + toPreparedBase(field.name.identifier)
|
||||
}
|
||||
|
||||
val returnType = if (accessors.chain) classDescriptor.defaultType else classDescriptor.builtIns.unitType
|
||||
|
||||
return classDescriptor.createFunction(
|
||||
Name.identifier(functionName),
|
||||
listOf(ValueParameter(field.name, field.type)),
|
||||
returnType,
|
||||
visibility = getter.visibility
|
||||
)
|
||||
}
|
||||
}
|
||||
+35
-7
@@ -8,32 +8,60 @@ 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.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
internal fun createFunction(
|
||||
containingClass: ClassDescriptor,
|
||||
internal data class ValueParameter(val name: Name, val type: KotlinType)
|
||||
|
||||
internal fun ClassDescriptor.createFunction(
|
||||
name: Name,
|
||||
valueParameters: List<ValueParameterDescriptor>,
|
||||
valueParameters: List<ValueParameter>,
|
||||
returnType: KotlinType?,
|
||||
modality: Modality? = Modality.OPEN,
|
||||
visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC
|
||||
): SimpleFunctionDescriptor {
|
||||
val methodDescriptor = SimpleFunctionDescriptorImpl.create(
|
||||
containingClass,
|
||||
this,
|
||||
Annotations.EMPTY,
|
||||
name,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
containingClass.source
|
||||
this.source
|
||||
)
|
||||
|
||||
val paramDescriptors = valueParameters.mapIndexed { idx, param -> methodDescriptor.makeValueParameter(param, idx) }
|
||||
|
||||
methodDescriptor.initialize(
|
||||
null,
|
||||
containingClass.thisAsReceiverParameter,
|
||||
this.thisAsReceiverParameter,
|
||||
mutableListOf(),
|
||||
valueParameters,
|
||||
paramDescriptors,
|
||||
returnType,
|
||||
modality,
|
||||
visibility
|
||||
)
|
||||
return methodDescriptor
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.makeValueParameter(param: ValueParameter, index: Int): ValueParameterDescriptor {
|
||||
return ValueParameterDescriptorImpl(
|
||||
containingDeclaration = this,
|
||||
original = null,
|
||||
index = index,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = param.name,
|
||||
outType = param.type,
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
varargElementType = null,
|
||||
source = this.source
|
||||
)
|
||||
}
|
||||
|
||||
internal fun ClassDescriptor.getVariables(): List<PropertyDescriptor> =
|
||||
this.unsubstitutedMemberScope.getVariableNames()
|
||||
.map {
|
||||
this.unsubstitutedMemberScope.getContributedVariables(it, NoLookupLocation.FROM_SYNTHETIC_SCOPE).single()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@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>>
|
||||
@@ -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
|
||||
|
||||
object Test {
|
||||
fun usage() {
|
||||
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")
|
||||
}
|
||||
|
||||
class OverridenGetterTest : SetterTest() {
|
||||
fun usage() {
|
||||
setName("abc")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//FILE: SetterTest.java
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Setter;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter @Setter
|
||||
public class SetterTest {
|
||||
private int age = 10;
|
||||
|
||||
private final String finalName = "zzz";
|
||||
|
||||
private boolean primitiveBoolean;
|
||||
|
||||
void test() {
|
||||
setAge(12);
|
||||
setPrimitiveBoolean(true);
|
||||
//no setters generated for final variable
|
||||
// setFinalName("adsf");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//FILE: test.kt
|
||||
|
||||
object Test {
|
||||
fun usage() {
|
||||
val obj = SetterTest()
|
||||
obj.setAge(42)
|
||||
obj.age = 42
|
||||
|
||||
obj.setPrimitiveBoolean(true)
|
||||
|
||||
|
||||
// no setters generated for final variable
|
||||
// obj.setFinalName("error")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//FILE: SetterTest.java
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Setter;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class SetterTest {
|
||||
@Accessors(fluent = true) private int fluent;
|
||||
|
||||
@Accessors(chain = true) private String chained;
|
||||
|
||||
@Accessors(chain = true, fluent = true) private String whyNotBoth;
|
||||
|
||||
|
||||
void test() {
|
||||
fluent(12);
|
||||
setChained("zz").getChained();
|
||||
whyNotBoth("zzz").whyNotBoth();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//FILE: test.kt
|
||||
|
||||
object Test {
|
||||
fun usage() {
|
||||
val obj = SetterTest()
|
||||
obj.fluent(12);
|
||||
obj.setChained("zz").getChained()
|
||||
obj.whyNotBoth("zzz").whyNotBoth()
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -51,6 +51,21 @@ public class LombokCompileTestGenerated extends AbstractLombokCompileTest {
|
||||
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("setters.kt")
|
||||
public void testSetters() throws Exception {
|
||||
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/setters.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("settersClassLevel.kt")
|
||||
public void testSettersClassLevel() throws Exception {
|
||||
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/settersClassLevel.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("settersVariations.kt")
|
||||
public void testSettersVariations() throws Exception {
|
||||
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/settersVariations.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/simple.kt");
|
||||
|
||||
Reference in New Issue
Block a user