[lombok] Support @RequiredArgsConstructor

This commit is contained in:
Andrey Zinovyev
2021-03-26 16:40:33 +03:00
committed by TeamCityServer
parent b336a335bf
commit afcb2ca904
11 changed files with 233 additions and 69 deletions
@@ -26,7 +26,7 @@ Features support:
[~] [@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor](https://projectlombok.org/features/constructor)
- [x] @NoArgsConstructor
- [x] @AllArgsConstructor
- [ ] @RequiredArgsConstructor
- [x] @RequiredArgsConstructor
[ ] [@Data](https://projectlombok.org/features/Data)
@@ -26,7 +26,8 @@ class LombokSyntheticJavaPartsProvider(private val config: LombokConfig) : Synth
SetterProcessor(config),
WithProcessor(config),
NoArgsConstructorProcessor(),
AllArgsConstructorProcessor()
AllArgsConstructorProcessor(),
RequiredArgsConstructorProcessor()
)
private val partsCache: MutableMap<ClassDescriptor, Parts> = WeakHashMap()
@@ -72,31 +72,51 @@ data class With(val visibility: DescriptorVisibility) {
}
}
data class NoArgsConstructor(
val visibility: DescriptorVisibility,
interface ConstructorAnnotation {
val visibility: DescriptorVisibility
val staticName: String?
) {
}
data class NoArgsConstructor(
override val visibility: DescriptorVisibility,
override val staticName: String?
) : ConstructorAnnotation {
companion object : AnnotationCompanion<NoArgsConstructor>() {
override val name: FqName = LombokNames.NO_ARGS_CONSTRUCTOR
override fun extract(annotation: AnnotationDescriptor): NoArgsConstructor =
NoArgsConstructor(
visibility = getVisibility(annotation),
visibility = getVisibility(annotation, "access"),
staticName = annotation.getNonBlankStringArgument("staticName")
)
}
}
data class AllArgsConstructor(
val visibility: DescriptorVisibility,
val staticName: String?
) {
override val visibility: DescriptorVisibility,
override val staticName: String?
) : ConstructorAnnotation {
companion object : AnnotationCompanion<AllArgsConstructor>() {
override val name: FqName = LombokNames.ALL_ARGS_CONSTRUCTOR
override fun extract(annotation: AnnotationDescriptor): AllArgsConstructor =
AllArgsConstructor(
visibility = getVisibility(annotation),
visibility = getVisibility(annotation, "access"),
staticName = annotation.getNonBlankStringArgument("staticName")
)
}
}
data class RequiredArgsConstructor(
override val visibility: DescriptorVisibility,
override val staticName: String?
) : ConstructorAnnotation {
companion object : AnnotationCompanion<RequiredArgsConstructor>() {
override val name: FqName = LombokNames.REQUIRED_ARGS_CONSTRUCTOR
override fun extract(annotation: AnnotationDescriptor): RequiredArgsConstructor =
RequiredArgsConstructor(
visibility = getVisibility(annotation, "access"),
staticName = annotation.getNonBlankStringArgument("staticName")
)
}
@@ -0,0 +1,53 @@
/*
* 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.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.lombok.config.AllArgsConstructor
import org.jetbrains.kotlin.lombok.config.AnnotationCompanion
import org.jetbrains.kotlin.lombok.config.ConstructorAnnotation
import org.jetbrains.kotlin.lombok.utils.ValueParameter
import org.jetbrains.kotlin.lombok.utils.createConstructor
import org.jetbrains.kotlin.lombok.utils.createFunction
import org.jetbrains.kotlin.lombok.utils.getJavaFields
import org.jetbrains.kotlin.name.Name
abstract class AbstractConstructorProcessor<A : ConstructorAnnotation>(
private val annotationCompanion: AnnotationCompanion<A>
) : Processor {
override fun contribute(classDescriptor: ClassDescriptor, jClass: JavaClassImpl): Parts {
val valueParameters = getPropertiesForParameters(classDescriptor).map { property ->
ValueParameter(property.name, property.type)
}
val result = annotationCompanion.getOrNull(classDescriptor)?.let { annotation ->
if (annotation.staticName == null) {
val constructor = classDescriptor.createConstructor(
valueParameters = valueParameters,
visibility = annotation.visibility
)
Parts(constructors = listOfNotNull(constructor))
} else {
val function = classDescriptor.createFunction(
Name.identifier(annotation.staticName!!),
valueParameters,
classDescriptor.defaultType,
visibility = annotation.visibility,
receiver = null
)
Parts(staticFunctions = listOf(function))
}
}
return result ?: Parts.Empty
}
protected abstract fun getPropertiesForParameters(classDescriptor: ClassDescriptor): List<PropertyDescriptor>
}
@@ -6,41 +6,12 @@
package org.jetbrains.kotlin.lombok.processor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.AllArgsConstructor
import org.jetbrains.kotlin.lombok.utils.ValueParameter
import org.jetbrains.kotlin.lombok.utils.createConstructor
import org.jetbrains.kotlin.lombok.utils.createFunction
import org.jetbrains.kotlin.lombok.utils.getJavaFields
import org.jetbrains.kotlin.name.Name
class AllArgsConstructorProcessor : Processor {
class AllArgsConstructorProcessor : AbstractConstructorProcessor<AllArgsConstructor>(AllArgsConstructor) {
override fun contribute(classDescriptor: ClassDescriptor, jClass: JavaClassImpl): Parts {
val valueParameters = classDescriptor.getJavaFields().map { property ->
ValueParameter(property.name, property.type)
}
val result = AllArgsConstructor.getOrNull(classDescriptor)?.let { annotation ->
if (annotation.staticName == null) {
val constructor = classDescriptor.createConstructor(
valueParameters = valueParameters,
visibility = annotation.visibility
)
Parts(constructors = listOfNotNull(constructor))
} else {
val function = classDescriptor.createFunction(
Name.identifier(annotation.staticName),
valueParameters,
classDescriptor.defaultType,
visibility = annotation.visibility,
receiver = null
)
Parts(staticFunctions = listOf(function))
}
}
return result ?: Parts.Empty
}
override fun getPropertiesForParameters(classDescriptor: ClassDescriptor): List<PropertyDescriptor> = classDescriptor.getJavaFields()
}
@@ -6,34 +6,10 @@
package org.jetbrains.kotlin.lombok.processor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.NoArgsConstructor
import org.jetbrains.kotlin.lombok.utils.createConstructor
import org.jetbrains.kotlin.lombok.utils.createFunction
import org.jetbrains.kotlin.name.Name
class NoArgsConstructorProcessor : Processor {
override fun contribute(classDescriptor: ClassDescriptor, jClass: JavaClassImpl): Parts {
val result = NoArgsConstructor.getOrNull(classDescriptor)?.let { annotation ->
if (annotation.staticName == null) {
val constructor = classDescriptor.createConstructor(
valueParameters = emptyList(),
visibility = annotation.visibility
)
Parts(constructors = listOfNotNull(constructor))
} else {
val function = classDescriptor.createFunction(
Name.identifier(annotation.staticName),
emptyList(),
classDescriptor.defaultType,
visibility = annotation.visibility,
receiver = null
)
Parts(staticFunctions = listOf(function))
}
}
return result ?: Parts.Empty
}
class NoArgsConstructorProcessor : AbstractConstructorProcessor<NoArgsConstructor>(NoArgsConstructor) {
override fun getPropertiesForParameters(classDescriptor: ClassDescriptor): List<PropertyDescriptor> = emptyList()
}
@@ -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.processor
import com.intellij.psi.PsiField
import com.intellij.psi.PsiModifier
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.RequiredArgsConstructor
import org.jetbrains.kotlin.lombok.utils.LombokNames
import org.jetbrains.kotlin.lombok.utils.getJavaFields
import org.jetbrains.kotlin.resolve.source.getPsi
class RequiredArgsConstructorProcessor : AbstractConstructorProcessor<RequiredArgsConstructor>(RequiredArgsConstructor) {
override fun getPropertiesForParameters(classDescriptor: ClassDescriptor): List<PropertyDescriptor> =
classDescriptor.getJavaFields().filter(this::isFieldRequired)
private fun isFieldRequired(field: PropertyDescriptor): Boolean {
val psi = field.source.getPsi()!! as PsiField
val final = psi.modifierList?.hasModifierProperty(PsiModifier.FINAL) ?: false ||
field.annotations.any { annotation -> LombokNames.NON_NULL_ANNOTATIONS.contains(annotation.fqName) }
return final && !psi.hasInitializer()
}
}
@@ -13,7 +13,27 @@ object LombokNames {
val GETTER = FqName("lombok.Getter")
val SETTER = FqName("lombok.Setter")
val WITH = FqName("lombok.With")
val VALUE = FqName("lombok.lombok.Value")
val NO_ARGS_CONSTRUCTOR = FqName("lombok.NoArgsConstructor")
val ALL_ARGS_CONSTRUCTOR = FqName("lombok.AllArgsConstructor")
val REQUIRED_ARGS_CONSTRUCTOR = FqName("lombok.RequiredArgsConstructor")
//taken from idea lombok plugin
val NON_NULL_ANNOTATIONS = listOf(
"androidx.annotation.NonNull",
"android.support.annotation.NonNull",
"com.sun.istack.internal.NotNull",
"edu.umd.cs.findbugs.annotations.NonNull",
"javax.annotation.Nonnull",
"lombok.NonNull",
"org.checkerframework.checker.nullness.qual.NonNull",
"org.eclipse.jdt.annotation.NonNull",
"org.eclipse.jgit.annotations.NonNull",
"org.jetbrains.annotations.NotNull",
"org.jmlspecs.annotation.NonNull",
"org.netbeans.api.annotations.common.NonNull",
"org.springframework.lang.NonNull"
).map { FqName(it) }.toSet()
}
@@ -0,0 +1,40 @@
//FILE: ConstructorExample.java
import lombok.*;
@RequiredArgsConstructor
public class ConstructorExample {
//not required
@Getter @Setter private int age;
//required final
@Getter @Setter private final String foo;
//not required final, because has initializer
@Getter @Setter private final int bar = 234;
//not required
@Getter(AccessLevel.PROTECTED) private String name;
//required by annotation
@NonNull
private boolean otherField;
//not required by annotation, because has initializer
@NonNull
private Long zzzz = 23L;
static void javaUsage() {
ConstructorExample generated = new ConstructorExample("foo", true);
}
}
//FILE: test.kt
object Test {
fun usage() {
val generated = ConstructorExample("foo", true)
}
}
@@ -0,0 +1,42 @@
//FILE: ConstructorExample.java
import lombok.*;
@RequiredArgsConstructor(staticName = "build")
public class ConstructorExample {
//not required
@Getter @Setter private int age;
//required final
@Getter @Setter private final String foo;
//not required final, because has initializer
@Getter @Setter private final int bar = 234;
//not required
@Getter(AccessLevel.PROTECTED) private String name;
//required by annotation
@NonNull
private boolean otherField;
//not required by annotation, because has initializer
@NonNull
private Long zzzz = 23L;
@NonNull Integer somethingElse;
static void javaUsage() {
ConstructorExample generated = ConstructorExample.build("foo", true, 12);
}
}
//FILE: test.kt
object Test {
fun usage() {
val generated = ConstructorExample.build("foo", true, 12)
}
}
@@ -66,6 +66,16 @@ public class LombokCompileTestGenerated extends AbstractLombokCompileTest {
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/noArgsConstructorStatic.kt");
}
@TestMetadata("requiredArgsConstructor.kt")
public void testRequiredArgsConstructor() throws Exception {
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/requiredArgsConstructor.kt");
}
@TestMetadata("requiredArgsConstructorStatic.kt")
public void testRequiredArgsConstructorStatic() throws Exception {
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/requiredArgsConstructorStatic.kt");
}
@TestMetadata("getters.kt")
public void testGetters() throws Exception {
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt");