[Lombok] Fix behavior of ignoreNullCollections parameter of @Singular

^KT-53724 Fixed
^KT-53721 Fixed
This commit is contained in:
Dmitriy Novozhilov
2022-09-09 12:36:07 +03:00
committed by teamcity
parent 2aadaee69f
commit 4ab79ed97d
4 changed files with 99 additions and 40 deletions
@@ -152,7 +152,7 @@ class BuilderProcessor(private val config: LombokConfig) : Processor {
when (typeName) { when (typeName) {
in LombokNames.SUPPORTED_COLLECTIONS -> { in LombokNames.SUPPORTED_COLLECTIONS -> {
val parameterType = field.parameterType(0, singular.allowNull) ?: return val parameterType = field.parameterType(0) ?: return
valueParameters = listOf( valueParameters = listOf(
LombokValueParameter(nameInSingularForm, parameterType) LombokValueParameter(nameInSingularForm, parameterType)
) )
@@ -163,28 +163,27 @@ class BuilderProcessor(private val config: LombokConfig) : Processor {
else -> builtIns.collection.defaultType else -> builtIns.collection.defaultType
} }
addMultipleParameterType = baseType.replace( addMultipleParameterType = baseType.withProperNullability(singular.allowNull)
newArguments = listOf(TypeProjectionImpl(parameterType)) .replace(newArguments = listOf(TypeProjectionImpl(parameterType)),)
)
} }
in LombokNames.SUPPORTED_MAPS -> { in LombokNames.SUPPORTED_MAPS -> {
val keyType = field.parameterType(0, singular.allowNull) ?: return val keyType = field.parameterType(0) ?: return
val valueType = field.parameterType(1, singular.allowNull) ?: return val valueType = field.parameterType(1) ?: return
valueParameters = listOf( valueParameters = listOf(
LombokValueParameter(Name.identifier("key"), keyType), LombokValueParameter(Name.identifier("key"), keyType),
LombokValueParameter(Name.identifier("value"), valueType), LombokValueParameter(Name.identifier("value"), valueType),
) )
addMultipleParameterType = field.module.builtIns.map.defaultType.replace( addMultipleParameterType = field.module.builtIns.map.defaultType
newArguments = listOf(TypeProjectionImpl(keyType), TypeProjectionImpl(valueType)) .withProperNullability(singular.allowNull)
) .replace(newArguments = listOf(TypeProjectionImpl(keyType), TypeProjectionImpl(valueType)))
} }
in LombokNames.SUPPORTED_TABLES -> { in LombokNames.SUPPORTED_TABLES -> {
val rowKeyType = field.parameterType(0, singular.allowNull) ?: return val rowKeyType = field.parameterType(0) ?: return
val columnKeyType = field.parameterType(1, singular.allowNull) ?: return val columnKeyType = field.parameterType(1) ?: return
val valueType = field.parameterType(2, singular.allowNull) ?: return val valueType = field.parameterType(2) ?: return
val tableDescriptor = field.module.resolveClassByFqName(LombokNames.TABLE, NoLookupLocation.FROM_SYNTHETIC_SCOPE) ?: return val tableDescriptor = field.module.resolveClassByFqName(LombokNames.TABLE, NoLookupLocation.FROM_SYNTHETIC_SCOPE) ?: return
@@ -194,13 +193,15 @@ class BuilderProcessor(private val config: LombokConfig) : Processor {
LombokValueParameter(Name.identifier("value"), valueType), LombokValueParameter(Name.identifier("value"), valueType),
) )
addMultipleParameterType = tableDescriptor.defaultType.replace( addMultipleParameterType = tableDescriptor.defaultType
newArguments = listOf( .withProperNullability(singular.allowNull)
TypeProjectionImpl(rowKeyType), .replace(
TypeProjectionImpl(columnKeyType), newArguments = listOf(
TypeProjectionImpl(valueType), TypeProjectionImpl(rowKeyType),
TypeProjectionImpl(columnKeyType),
TypeProjectionImpl(valueType),
)
) )
)
} }
else -> return else -> return
@@ -242,17 +243,20 @@ class BuilderProcessor(private val config: LombokConfig) : Processor {
private class BuilderData(val builder: Builder, val constructingClass: ClassDescriptor) private class BuilderData(val builder: Builder, val constructingClass: ClassDescriptor)
private fun PropertyDescriptor.parameterType(index: Int, allowNull: Boolean): KotlinType? { private fun PropertyDescriptor.parameterType(index: Int): KotlinType? {
val type = returnType?.arguments?.getOrNull(index)?.type ?: return null val type = returnType?.arguments?.getOrNull(index)?.type ?: return null
val typeWithProperNullability = if (allowNull) type.makeNullable() else type.makeNotNullable() return type.replaceAnnotations(
return typeWithProperNullability.replaceAnnotations(
CompositeAnnotations( CompositeAnnotations(
typeWithProperNullability.annotations, type.annotations,
ENHANCED_NULLABILITY_ANNOTATIONS ENHANCED_NULLABILITY_ANNOTATIONS
) )
) )
} }
private fun KotlinType.withProperNullability(allowNull: Boolean): KotlinType {
return if (allowNull) makeNullable() else makeNotNullable()
}
private fun Name.toMethodName(builder: Builder): Name { private fun Name.toMethodName(builder: Builder): Name {
val prefix = builder.setterPrefix val prefix = builder.setterPrefix
return if (prefix.isNullOrBlank()) { return if (prefix.isNullOrBlank()) {
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaClassifierType import org.jetbrains.kotlin.load.java.structure.JavaClassifierType
import org.jetbrains.kotlin.load.java.structure.JavaType import org.jetbrains.kotlin.load.java.structure.JavaType
import org.jetbrains.kotlin.lombok.config.LombokConfig
import org.jetbrains.kotlin.lombok.k2.* import org.jetbrains.kotlin.lombok.k2.*
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Builder import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Builder
import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Singular import org.jetbrains.kotlin.lombok.k2.config.ConeLombokAnnotations.Singular
@@ -189,7 +188,7 @@ class BuilderGenerator(session: FirSession) : FirDeclarationGenerationExtension(
when (typeName) { when (typeName) {
in LombokNames.SUPPORTED_COLLECTIONS -> { in LombokNames.SUPPORTED_COLLECTIONS -> {
val parameterType = javaClassifierType.parameterType(0, singular.allowNull) ?: return val parameterType = javaClassifierType.parameterType(0) ?: return
valueParameters = listOf( valueParameters = listOf(
ConeLombokValueParameter(nameInSingularForm, parameterType.toRef()) ConeLombokValueParameter(nameInSingularForm, parameterType.toRef())
) )
@@ -199,24 +198,28 @@ class BuilderGenerator(session: FirSession) : FirDeclarationGenerationExtension(
else -> JavaClasses.Collection else -> JavaClasses.Collection
} }
addMultipleParameterType = DummyJavaClassType(baseType, typeArguments = listOf(parameterType)).toRef() addMultipleParameterType = DummyJavaClassType(baseType, typeArguments = listOf(parameterType))
.withProperNullability(singular.allowNull)
.toRef()
} }
in LombokNames.SUPPORTED_MAPS -> { in LombokNames.SUPPORTED_MAPS -> {
val keyType = javaClassifierType.parameterType(0, singular.allowNull) ?: return val keyType = javaClassifierType.parameterType(0) ?: return
val valueType = javaClassifierType.parameterType(1, singular.allowNull) ?: return val valueType = javaClassifierType.parameterType(1) ?: return
valueParameters = listOf( valueParameters = listOf(
ConeLombokValueParameter(Name.identifier("key"), keyType.toRef()), ConeLombokValueParameter(Name.identifier("key"), keyType.toRef()),
ConeLombokValueParameter(Name.identifier("value"), valueType.toRef()), ConeLombokValueParameter(Name.identifier("value"), valueType.toRef()),
) )
addMultipleParameterType = DummyJavaClassType(JavaClasses.Map, typeArguments = listOf(keyType, valueType)).toRef() addMultipleParameterType = DummyJavaClassType(JavaClasses.Map, typeArguments = listOf(keyType, valueType))
.withProperNullability(singular.allowNull)
.toRef()
} }
in LombokNames.SUPPORTED_TABLES -> { in LombokNames.SUPPORTED_TABLES -> {
val rowKeyType = javaClassifierType.parameterType(0, singular.allowNull) ?: return val rowKeyType = javaClassifierType.parameterType(0) ?: return
val columnKeyType = javaClassifierType.parameterType(1, singular.allowNull) ?: return val columnKeyType = javaClassifierType.parameterType(1) ?: return
val valueType = javaClassifierType.parameterType(2, singular.allowNull) ?: return val valueType = javaClassifierType.parameterType(2) ?: return
valueParameters = listOf( valueParameters = listOf(
ConeLombokValueParameter(Name.identifier("rowKey"), rowKeyType.toRef()), ConeLombokValueParameter(Name.identifier("rowKey"), rowKeyType.toRef()),
@@ -227,7 +230,7 @@ class BuilderGenerator(session: FirSession) : FirDeclarationGenerationExtension(
addMultipleParameterType = DummyJavaClassType( addMultipleParameterType = DummyJavaClassType(
JavaClasses.Table, JavaClasses.Table,
typeArguments = listOf(rowKeyType, columnKeyType, valueType) typeArguments = listOf(rowKeyType, columnKeyType, valueType)
).toRef() ).withProperNullability(singular.allowNull).toRef()
} }
else -> return else -> return
@@ -273,9 +276,12 @@ class BuilderGenerator(session: FirSession) : FirDeclarationGenerationExtension(
private val String.singularForm: String? private val String.singularForm: String?
get() = StringUtil.unpluralize(this) get() = StringUtil.unpluralize(this)
private fun JavaClassifierType.parameterType(index: Int, allowNull: Boolean): JavaType? { private fun JavaClassifierType.parameterType(index: Int): JavaType? {
val type = typeArguments.getOrNull(index) ?: return null return typeArguments.getOrNull(index)
return if (allowNull) type.makeNullable() else type.makeNotNullable() }
private fun JavaType.withProperNullability(allowNull: Boolean): JavaType {
return if (allowNull) makeNullable() else makeNotNullable()
} }
} }
@@ -10,6 +10,7 @@ import lombok.Singular;
@Data @Data
public class User { public class User {
@Singular private java.util.List<String> names; @Singular private java.util.List<String> names;
@Singular private java.util.Map<Integer, Integer> pairs;
} }
// FILE: UserWithoutNull.java // FILE: UserWithoutNull.java
@@ -21,6 +22,7 @@ import lombok.Singular;
@Data @Data
public class UserWithoutNull { public class UserWithoutNull {
@Singular(ignoreNullCollections = false) private java.util.List<String> names; @Singular(ignoreNullCollections = false) private java.util.List<String> names;
@Singular(ignoreNullCollections = false) private java.util.Map<Integer, Integer> pairs;
} }
// FILE: UserWithNull.java // FILE: UserWithNull.java
@@ -32,19 +34,54 @@ import lombok.Singular;
@Data @Data
public class UserWithNull { public class UserWithNull {
@Singular(ignoreNullCollections = true) private java.util.List<String> names; @Singular(ignoreNullCollections = true) private java.util.List<String> names;
@Singular(ignoreNullCollections = true) private java.util.Map<Integer, Integer> pairs;
} }
// FILE: test.kt // FILE: test.kt
fun test() { fun test_1() {
User.builder() User.builder()
.name("User") .name("User")
.name(<!NULL_FOR_NONNULL_TYPE!>null<!>) // error .name(null)
.names(listOf("other"))
.names(listOf(null))
.names(<!NULL_FOR_NONNULL_TYPE!>null<!>)
.pair(null, 1)
.pair(1, null)
.pairs(mapOf(1 to 1))
.pairs(mapOf(null to 1))
.pairs(mapOf(1 to null))
.pairs(mapOf(null to null))
.pairs(<!NULL_FOR_NONNULL_TYPE!>null<!>)
}
fun test_2() {
UserWithoutNull.builder() UserWithoutNull.builder()
.name("User") .name("User")
.name(<!NULL_FOR_NONNULL_TYPE!>null<!>) // error .name(null)
.names(listOf("other"))
.names(listOf(null))
.names(<!NULL_FOR_NONNULL_TYPE!>null<!>)
.pair(null, 1)
.pair(1, null)
.pairs(mapOf(1 to 1))
.pairs(mapOf(null to 1))
.pairs(mapOf(1 to null))
.pairs(mapOf(null to null))
.pairs(<!NULL_FOR_NONNULL_TYPE!>null<!>)
}
fun test_3() {
UserWithNull.builder() UserWithNull.builder()
.name("User") .name("User")
.name(null) // ok .name(null)
.names(listOf("other"))
.names(listOf(null))
.names(null)
.pair(null, 1)
.pair(1, null)
.pairs(mapOf(1 to 1))
.pairs(mapOf(null to 1))
.pairs(mapOf(1 to null))
.pairs(mapOf(null to null))
.pairs(null)
} }
@@ -1,15 +1,20 @@
package package
public fun </*0*/ T> assertEquals(/*0*/ a: T, /*1*/ b: T): kotlin.Unit public fun </*0*/ T> assertEquals(/*0*/ a: T, /*1*/ b: T): kotlin.Unit
public fun test(): kotlin.Unit public fun test_1(): kotlin.Unit
public fun test_2(): kotlin.Unit
public fun test_3(): kotlin.Unit
@lombok.Builder @lombok.Data public open class User { @lombok.Builder @lombok.Data public open class User {
public constructor User() public constructor User()
@lombok.Singular private final var names: kotlin.collections.(Mutable)List<kotlin.String!>! @lombok.Singular private final var names: kotlin.collections.(Mutable)List<kotlin.String!>!
@lombok.Singular private final var pairs: kotlin.collections.(Mutable)Map<kotlin.Int!, kotlin.Int!>!
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open /*synthesized*/ fun getNames(): kotlin.collections.(Mutable)List<kotlin.String!>! public open /*synthesized*/ fun getNames(): kotlin.collections.(Mutable)List<kotlin.String!>!
public open /*synthesized*/ fun getPairs(): kotlin.collections.(Mutable)Map<kotlin.Int!, kotlin.Int!>!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open /*synthesized*/ fun setNames(/*0*/ names: kotlin.collections.(Mutable)List<kotlin.String!>!): kotlin.Unit public open /*synthesized*/ fun setNames(/*0*/ names: kotlin.collections.(Mutable)List<kotlin.String!>!): kotlin.Unit
public open /*synthesized*/ fun setPairs(/*0*/ pairs: kotlin.collections.(Mutable)Map<kotlin.Int!, kotlin.Int!>!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members // Static members
@@ -19,10 +24,13 @@ public fun test(): kotlin.Unit
@lombok.Builder @lombok.Data public open class UserWithNull { @lombok.Builder @lombok.Data public open class UserWithNull {
public constructor UserWithNull() public constructor UserWithNull()
@lombok.Singular(ignoreNullCollections = true) private final var names: kotlin.collections.(Mutable)List<kotlin.String!>! @lombok.Singular(ignoreNullCollections = true) private final var names: kotlin.collections.(Mutable)List<kotlin.String!>!
@lombok.Singular(ignoreNullCollections = true) private final var pairs: kotlin.collections.(Mutable)Map<kotlin.Int!, kotlin.Int!>!
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open /*synthesized*/ fun getNames(): kotlin.collections.(Mutable)List<kotlin.String!>! public open /*synthesized*/ fun getNames(): kotlin.collections.(Mutable)List<kotlin.String!>!
public open /*synthesized*/ fun getPairs(): kotlin.collections.(Mutable)Map<kotlin.Int!, kotlin.Int!>!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open /*synthesized*/ fun setNames(/*0*/ names: kotlin.collections.(Mutable)List<kotlin.String!>!): kotlin.Unit public open /*synthesized*/ fun setNames(/*0*/ names: kotlin.collections.(Mutable)List<kotlin.String!>!): kotlin.Unit
public open /*synthesized*/ fun setPairs(/*0*/ pairs: kotlin.collections.(Mutable)Map<kotlin.Int!, kotlin.Int!>!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members // Static members
@@ -32,12 +40,16 @@ public fun test(): kotlin.Unit
@lombok.Builder @lombok.Data public open class UserWithoutNull { @lombok.Builder @lombok.Data public open class UserWithoutNull {
public constructor UserWithoutNull() public constructor UserWithoutNull()
@lombok.Singular(ignoreNullCollections = false) private final var names: kotlin.collections.(Mutable)List<kotlin.String!>! @lombok.Singular(ignoreNullCollections = false) private final var names: kotlin.collections.(Mutable)List<kotlin.String!>!
@lombok.Singular(ignoreNullCollections = false) private final var pairs: kotlin.collections.(Mutable)Map<kotlin.Int!, kotlin.Int!>!
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open /*synthesized*/ fun getNames(): kotlin.collections.(Mutable)List<kotlin.String!>! public open /*synthesized*/ fun getNames(): kotlin.collections.(Mutable)List<kotlin.String!>!
public open /*synthesized*/ fun getPairs(): kotlin.collections.(Mutable)Map<kotlin.Int!, kotlin.Int!>!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open /*synthesized*/ fun setNames(/*0*/ names: kotlin.collections.(Mutable)List<kotlin.String!>!): kotlin.Unit public open /*synthesized*/ fun setNames(/*0*/ names: kotlin.collections.(Mutable)List<kotlin.String!>!): kotlin.Unit
public open /*synthesized*/ fun setPairs(/*0*/ pairs: kotlin.collections.(Mutable)Map<kotlin.Int!, kotlin.Int!>!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members // Static members
public final /*synthesized*/ fun builder(): UserWithoutNull.UserWithoutNullBuilder public final /*synthesized*/ fun builder(): UserWithoutNull.UserWithoutNullBuilder
} }