[Lombok] Constructor can have only non-final or not initialized fields

When using the AllArgsConstructor annotation (directly or via
meta-annotation), only fields that are not final or not initialized
should be added as arguments. Previously, all fields were being included
regardless of modality or initialization, which is not consistent with
the behavior of Lombok.

^KT-54054 Fixed
This commit is contained in:
Brian Norman
2024-01-11 13:30:00 -06:00
committed by Space Team
parent cf4c55e02d
commit 2ab1e712f8
4 changed files with 36 additions and 5 deletions
@@ -5,11 +5,14 @@
package org.jetbrains.kotlin.lombok.processor 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.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.AllArgsConstructor import org.jetbrains.kotlin.lombok.config.LombokAnnotations.AllArgsConstructor
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Value import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Value
import org.jetbrains.kotlin.lombok.utils.getJavaFields import org.jetbrains.kotlin.lombok.utils.getJavaFields
import org.jetbrains.kotlin.resolve.source.getPsi
class AllArgsConstructorProcessor : AbstractConstructorProcessor<AllArgsConstructor>() { class AllArgsConstructorProcessor : AbstractConstructorProcessor<AllArgsConstructor>() {
@@ -17,6 +20,12 @@ class AllArgsConstructorProcessor : AbstractConstructorProcessor<AllArgsConstruc
AllArgsConstructor.getOrNull(classDescriptor) ?: Value.getOrNull(classDescriptor)?.asAllArgsConstructor() AllArgsConstructor.getOrNull(classDescriptor) ?: Value.getOrNull(classDescriptor)?.asAllArgsConstructor()
override fun getPropertiesForParameters(classDescriptor: ClassDescriptor): List<PropertyDescriptor> = override fun getPropertiesForParameters(classDescriptor: ClassDescriptor): List<PropertyDescriptor> =
classDescriptor.getJavaFields() classDescriptor.getJavaFields().filter(this::isFieldAllowed)
private fun isFieldAllowed(field: PropertyDescriptor): Boolean {
val psi = field.source.getPsi() as? PsiField ?: return true
val final = psi.modifierList?.hasModifierProperty(PsiModifier.FINAL) ?: false
return !final || !psi.hasInitializer()
}
} }
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.lombok.k2.generators package org.jetbrains.kotlin.lombok.k2.generators
import com.intellij.psi.PsiField
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.AllArgsConstructor import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.AllArgsConstructor
import org.jetbrains.kotlin.psi
class AllArgsConstructorGeneratorPart(session: FirSession) : AbstractConstructorGeneratorPart<AllArgsConstructor>(session) { class AllArgsConstructorGeneratorPart(session: FirSession) : AbstractConstructorGeneratorPart<AllArgsConstructor>(session) {
override fun getConstructorInfo(classSymbol: FirClassSymbol<*>): AllArgsConstructor? { override fun getConstructorInfo(classSymbol: FirClassSymbol<*>): AllArgsConstructor? {
@@ -19,6 +21,14 @@ class AllArgsConstructorGeneratorPart(session: FirSession) : AbstractConstructor
@OptIn(SymbolInternals::class) @OptIn(SymbolInternals::class)
override fun getFieldsForParameters(classSymbol: FirClassSymbol<*>): List<FirJavaField> { override fun getFieldsForParameters(classSymbol: FirClassSymbol<*>): List<FirJavaField> {
return classSymbol.fir.declarations.filterIsInstance<FirJavaField>() return classSymbol.fir.declarations
.filterIsInstance<FirJavaField>()
.filter { it.isFieldAllowed() }
}
private fun FirJavaField.isFieldAllowed(): Boolean {
// TODO: consider adding `hasInitializer` property directly to java model
val hasInitializer = (source?.psi as? PsiField)?.hasInitializer() ?: false
return isVar || !hasInitializer
} }
} }
+8 -2
View File
@@ -5,11 +5,17 @@ import lombok.*;
@AllArgsConstructor @AllArgsConstructor
public class ConstructorExample { public class ConstructorExample {
// Part of constructor because not final.
@Getter @Setter private int age = 10; @Getter @Setter private int age = 10;
// Part of constructor because not initialized.
@Getter(AccessLevel.PROTECTED) private String name; @Getter(AccessLevel.PROTECTED) private String name;
private boolean otherField; // Part of constructor because not initialized.
private final boolean otherField;
// Not part of constructor because final and initialized.
@Getter private final String result = "OK";
static void javaUsage() { static void javaUsage() {
val generated = new ConstructorExample(12, "sdf", true); val generated = new ConstructorExample(12, "sdf", true);
@@ -21,5 +27,5 @@ public class ConstructorExample {
fun box(): String { fun box(): String {
val generated = ConstructorExample(12, "sdf", true) val generated = ConstructorExample(12, "sdf", true)
return "OK" return generated.getResult()
} }
+7 -1
View File
@@ -5,12 +5,18 @@ import lombok.*;
@AllArgsConstructor(staticName = "of") @AllArgsConstructor(staticName = "of")
public class ConstructorExample { public class ConstructorExample {
// Part of constructor because not final.
@Getter @Setter private int age = 10; @Getter @Setter private int age = 10;
// Part of constructor because not initialized.
@Getter(AccessLevel.PROTECTED) private String name; @Getter(AccessLevel.PROTECTED) private String name;
// Part of constructor because not initialized.
private boolean otherField; private boolean otherField;
// Not part of constructor because final and initialized.
@Getter private final String result = "OK";
public ConstructorExample(String arg) { public ConstructorExample(String arg) {
} }
@@ -27,5 +33,5 @@ public class ConstructorExample {
fun box(): String { fun box(): String {
val existing: ConstructorExample = ConstructorExample("existing") val existing: ConstructorExample = ConstructorExample("existing")
val generated: ConstructorExample = ConstructorExample.of(45, "234", false) val generated: ConstructorExample = ConstructorExample.of(45, "234", false)
return "OK" return generated.getResult()
} }