diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 1971e11..13bfecc 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -16,17 +16,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/setup-java@v2 - with: - distribution: 'zulu' - java-version: '19' - - uses: gradle/gradle-build-action@v2 - with: - arguments: check --continue --stacktrace -Pkotshi.createAnnotationsUsingConstructor=false - testWithAnnotations: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - uses: actions/setup-java@v2 with: distribution: 'zulu' @@ -34,17 +23,6 @@ jobs: - uses: gradle/gradle-build-action@v2 with: arguments: check --continue --stacktrace - testWithLegacyDataClassRenderer: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v2 - with: - distribution: 'zulu' - java-version: '19' - - uses: gradle/gradle-build-action@v2 - with: - arguments: check --continue --stacktrace -Pkotshi.useLegacyDataClassRenderer=true testOldestSupportedMoshi: runs-on: ubuntu-latest steps: @@ -58,7 +36,7 @@ jobs: arguments: check --continue --stacktrace -Pkotshi.internal.useLegacyMoshi=true deploySnapshot: runs-on: ubuntu-latest - needs: [test, testWithAnnotations, testWithLegacyDataClassRenderer, testOldestSupportedMoshi] + needs: [test, testOldestSupportedMoshi] steps: - uses: actions/checkout@v2 - uses: actions/setup-java@v2 diff --git a/README.md b/README.md index 186c90b..1d75ecb 100644 --- a/README.md +++ b/README.md @@ -106,69 +106,6 @@ that extends `JsonAdapter` with `@RegisterJsonAdapter` and Kotshi will generate ### Options -#### `kotshi.createAnnotationsUsingConstructor` -This option enables a new way of creating annotations instances at runtime. Normally Kotshi uses reflection to create -the qualifier annotations but as of 1.5.30 of Kotlin you can enable creating annotations by calling the constructor. - -This behavior is enabled by default when using language version 1.6 but can be explicitly enabled or disabled using this -option. - -Examples: -
- KSP - - ```kotlin - ksp { - arg("kotshi.createAnnotationsUsingConstructor", "false") - } - ``` -
- - -
- KAPT - - ```kotlin - kapt { - arguments { - arg("kotshi.createAnnotationsUsingConstructor", false) - } - } - ``` -
- -See more about instantiating annotations here: https://kotlinlang.org/docs/whatsnew1530.html#instantiation-of-annotation-classes - -#### `kotshi.useLegacyDataClassRenderer` -This option allows you to use the old way of creating classes with parameters that has default values. - -From 2.10.0 reflection is used create data classes, but by setting this option to `true` the old behavior can be used -instead which used the `copy` method. - -Examples: -
- KSP - - ```kotlin - ksp { - arg("kotshi.createAnnotationsUsingConstructor", "false") - } - ``` -
- - -
- KAPT - - ```kotlin - kapt { - arguments { - arg("kotshi.useLegacyDataClassRenderer", true) - } - } - ``` -
- #### `kotshi.generatedAnnotation` This option tells Kotshi to add the `@Generated` annotation to all generated classes which is disabled by default. diff --git a/compiler/src/main/kotlin/se/ansman/kotshi/Options.kt b/compiler/src/main/kotlin/se/ansman/kotshi/Options.kt index 7c30e5a..6122716 100644 --- a/compiler/src/main/kotlin/se/ansman/kotshi/Options.kt +++ b/compiler/src/main/kotlin/se/ansman/kotshi/Options.kt @@ -1,8 +1,6 @@ package se.ansman.kotshi object Options { - const val createAnnotationsUsingConstructor = "kotshi.createAnnotationsUsingConstructor" - const val useLegacyDataClassRenderer = "kotshi.useLegacyDataClassRenderer" const val generatedAnnotation = "kotshi.generatedAnnotation" val possibleGeneratedAnnotations = setOf(Types.Java.generatedJDK9, Types.Java.generatedPreJDK9) diff --git a/compiler/src/main/kotlin/se/ansman/kotshi/kapt/AdaptersProcessingStep.kt b/compiler/src/main/kotlin/se/ansman/kotshi/kapt/AdaptersProcessingStep.kt index 8d3873e..93a4ab0 100644 --- a/compiler/src/main/kotlin/se/ansman/kotshi/kapt/AdaptersProcessingStep.kt +++ b/compiler/src/main/kotlin/se/ansman/kotshi/kapt/AdaptersProcessingStep.kt @@ -38,8 +38,6 @@ class AdaptersProcessingStep( private val types: Types, private val elements: Elements, private val generatedAnnotation: GeneratedAnnotation?, - private val createAnnotationsUsingConstructor: Boolean?, - private val useLegacyDataClassRenderer: Boolean, ) : KotshiProcessor.GeneratingProcessingStep() { override val annotations: Set> = setOf( @@ -146,8 +144,6 @@ class AdaptersProcessingStep( adapters += generator.generateAdapter( generatedAnnotation = generatedAnnotation, filer = filer, - createAnnotationsUsingConstructor = createAnnotationsUsingConstructor, - useLegacyDataClassRenderer = useLegacyDataClassRenderer ) } catch (e: KaptProcessingError) { messager.logKotshiError(e) diff --git a/compiler/src/main/kotlin/se/ansman/kotshi/kapt/FactoryProcessingStep.kt b/compiler/src/main/kotlin/se/ansman/kotshi/kapt/FactoryProcessingStep.kt index ede8014..c87b581 100644 --- a/compiler/src/main/kotlin/se/ansman/kotshi/kapt/FactoryProcessingStep.kt +++ b/compiler/src/main/kotlin/se/ansman/kotshi/kapt/FactoryProcessingStep.kt @@ -51,7 +51,6 @@ class FactoryProcessingStep( private val generatedAnnotation: GeneratedAnnotation?, private val generatedAdapters: List>, private val metadataAccessor: MetadataAccessor, - private val createAnnotationsUsingConstructor: Boolean?, ) : KotshiProcessor.GeneratingProcessingStep() { @OptIn(ExperimentalKotshiApi::class) @@ -101,9 +100,7 @@ class FactoryProcessingStep( manuallyRegisteredAdapters = manuallyRegisteredAdapters, ) - val createAnnotationsUsingConstructor = - createAnnotationsUsingConstructor - ?: metadataAccessor.getMetadata(element).supportsCreatingAnnotationsWithConstructor + val createAnnotationsUsingConstructor = metadataAccessor.getMetadata(element).supportsCreatingAnnotationsWithConstructor JsonAdapterFactoryRenderer(factory, createAnnotationsUsingConstructor) .render(generatedAnnotation) { diff --git a/compiler/src/main/kotlin/se/ansman/kotshi/kapt/KotshiProcessor.kt b/compiler/src/main/kotlin/se/ansman/kotshi/kapt/KotshiProcessor.kt index 1534011..cfa7fb7 100644 --- a/compiler/src/main/kotlin/se/ansman/kotshi/kapt/KotshiProcessor.kt +++ b/compiler/src/main/kotlin/se/ansman/kotshi/kapt/KotshiProcessor.kt @@ -27,8 +27,6 @@ import javax.lang.model.util.Types @AutoService(Processor::class) @IncrementalAnnotationProcessor(AGGREGATING) class KotshiProcessor : AbstractProcessor() { - private var createAnnotationsUsingConstructor: Boolean? = null - private var useLegacyDataClassRenderer: Boolean = false private var generatedAnnotation: GeneratedAnnotation? = null private lateinit var elements: Elements private lateinit var types: Types @@ -49,8 +47,6 @@ class KotshiProcessor : AbstractProcessor() { types = types, elements = processingEnv.elementUtils, generatedAnnotation = generatedAnnotation, - createAnnotationsUsingConstructor = createAnnotationsUsingConstructor, - useLegacyDataClassRenderer = useLegacyDataClassRenderer, ), FactoryProcessingStep( processor = this, @@ -61,7 +57,6 @@ class KotshiProcessor : AbstractProcessor() { generatedAnnotation = generatedAnnotation, generatedAdapters = adapters, metadataAccessor = metadataAccessor, - createAnnotationsUsingConstructor = createAnnotationsUsingConstructor, ) ) } @@ -69,8 +64,6 @@ class KotshiProcessor : AbstractProcessor() { @Synchronized override fun init(processingEnv: ProcessingEnvironment) { super.init(processingEnv) - createAnnotationsUsingConstructor = processingEnv.options[Options.createAnnotationsUsingConstructor]?.toBooleanStrict() - useLegacyDataClassRenderer = processingEnv.options[Options.useLegacyDataClassRenderer]?.toBooleanStrict() ?: useLegacyDataClassRenderer generatedAnnotation = processingEnv.options[Options.generatedAnnotation] ?.let { name -> Options.possibleGeneratedAnnotations[name] ?: run { @@ -95,7 +88,7 @@ class KotshiProcessor : AbstractProcessor() { override fun getSupportedAnnotationTypes(): Set = getSupportedAnnotationClasses().mapTo(mutableSetOf()) { it.canonicalName } - override fun getSupportedOptions(): Set = setOf("kotshi.createAnnotationsUsingConstructor") + override fun getSupportedOptions(): Set = setOf(Options.generatedAnnotation) override fun process(annotations: Set, roundEnv: RoundEnvironment): Boolean { if (!roundEnv.processingOver()) { diff --git a/compiler/src/main/kotlin/se/ansman/kotshi/kapt/generators/AdapterGenerator.kt b/compiler/src/main/kotlin/se/ansman/kotshi/kapt/generators/AdapterGenerator.kt index c8c8e47..61cf319 100644 --- a/compiler/src/main/kotlin/se/ansman/kotshi/kapt/generators/AdapterGenerator.kt +++ b/compiler/src/main/kotlin/se/ansman/kotshi/kapt/generators/AdapterGenerator.kt @@ -62,8 +62,6 @@ abstract class AdapterGenerator( fun generateAdapter( generatedAnnotation: GeneratedAnnotation?, filer: Filer, - createAnnotationsUsingConstructor: Boolean?, - useLegacyDataClassRenderer: Boolean, ): GeneratedAdapter { when { kmClass.isInner -> @@ -76,9 +74,7 @@ abstract class AdapterGenerator( val generatedAdapter = getGeneratableJsonAdapter() .createRenderer( - createAnnotationsUsingConstructor = createAnnotationsUsingConstructor - ?: metadataAccessor.getMetadata(targetElement).supportsCreatingAnnotationsWithConstructor, - useLegacyDataClassRenderer = useLegacyDataClassRenderer, + createAnnotationsUsingConstructor = metadataAccessor.getMetadata(targetElement).supportsCreatingAnnotationsWithConstructor, error = { KaptProcessingError(it, targetElement) }, ) .render(generatedAnnotation, originatingElement = targetElement) { diff --git a/compiler/src/main/kotlin/se/ansman/kotshi/ksp/KotshiSymbolProcessor.kt b/compiler/src/main/kotlin/se/ansman/kotshi/ksp/KotshiSymbolProcessor.kt index c7635fa..39e277c 100644 --- a/compiler/src/main/kotlin/se/ansman/kotshi/ksp/KotshiSymbolProcessor.kt +++ b/compiler/src/main/kotlin/se/ansman/kotshi/ksp/KotshiSymbolProcessor.kt @@ -49,13 +49,7 @@ import se.ansman.kotshi.model.findKotshiConstructor import se.ansman.kotshi.renderer.JsonAdapterFactoryRenderer class KotshiSymbolProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor { - private val createAnnotationsUsingConstructor = environment.options[Options.createAnnotationsUsingConstructor] - ?.toBooleanStrict() - ?: environment.kotlinVersion.isAtLeast(1, 6) - - private val useLegacyDataClassRenderer = environment.options[Options.useLegacyDataClassRenderer] - ?.toBooleanStrict() - ?: false + private val createAnnotationsUsingConstructor = environment.kotlinVersion.isAtLeast(1, 6) private val generatedAnnotation = environment.options[Options.generatedAnnotation] ?.let { name -> @@ -329,7 +323,6 @@ class KotshiSymbolProcessor(private val environment: SymbolProcessorEnvironment) generator.generateAdapter( createAnnotationsUsingConstructor = createAnnotationsUsingConstructor, - useLegacyDataClassRenderer = useLegacyDataClassRenderer, generatedAnnotation = generatedAnnotation, ) } catch (e: KspProcessingError) { diff --git a/compiler/src/main/kotlin/se/ansman/kotshi/ksp/generators/AdapterGenerator.kt b/compiler/src/main/kotlin/se/ansman/kotshi/ksp/generators/AdapterGenerator.kt index 1624189..7049e79 100644 --- a/compiler/src/main/kotlin/se/ansman/kotshi/ksp/generators/AdapterGenerator.kt +++ b/compiler/src/main/kotlin/se/ansman/kotshi/ksp/generators/AdapterGenerator.kt @@ -64,7 +64,6 @@ abstract class AdapterGenerator( fun generateAdapter( createAnnotationsUsingConstructor: Boolean, - useLegacyDataClassRenderer: Boolean, generatedAnnotation: GeneratedAnnotation?, ): GeneratedAdapter { when { @@ -81,7 +80,6 @@ abstract class AdapterGenerator( val generatedAdapter = getGeneratableJsonAdapter() .createRenderer( createAnnotationsUsingConstructor = createAnnotationsUsingConstructor, - useLegacyDataClassRenderer = useLegacyDataClassRenderer, error = { KspProcessingError(it, targetElement) }, ) .render(generatedAnnotation, originatingElement = targetElement.containingFile!!) { diff --git a/compiler/src/main/kotlin/se/ansman/kotshi/renderer/AdapterRenderer.kt b/compiler/src/main/kotlin/se/ansman/kotshi/renderer/AdapterRenderer.kt index a2c35e5..d5d50fe 100644 --- a/compiler/src/main/kotlin/se/ansman/kotshi/renderer/AdapterRenderer.kt +++ b/compiler/src/main/kotlin/se/ansman/kotshi/renderer/AdapterRenderer.kt @@ -145,16 +145,10 @@ abstract class AdapterRenderer(private val adapter: GeneratableJsonAdapter) { fun GeneratableJsonAdapter.createRenderer( createAnnotationsUsingConstructor: Boolean, - useLegacyDataClassRenderer: Boolean, error: (String) -> Throwable, ): AdapterRenderer = when (this) { - is DataClassJsonAdapter -> - if (useLegacyDataClassRenderer) { - LegacyDataClassAdapterRenderer(this, createAnnotationsUsingConstructor) - } else { - DataClassAdapterRenderer(this, createAnnotationsUsingConstructor) - } + is DataClassJsonAdapter -> DataClassAdapterRenderer(this, createAnnotationsUsingConstructor) is EnumJsonAdapter -> EnumAdapterRenderer(this) is ObjectJsonAdapter -> ObjectAdapterRenderer(this) is SealedClassJsonAdapter -> SealedClassAdapterRenderer(this, error) diff --git a/compiler/src/main/kotlin/se/ansman/kotshi/renderer/LegacyDataClassAdapterRenderer.kt b/compiler/src/main/kotlin/se/ansman/kotshi/renderer/LegacyDataClassAdapterRenderer.kt deleted file mode 100644 index fd6d8f5..0000000 --- a/compiler/src/main/kotlin/se/ansman/kotshi/renderer/LegacyDataClassAdapterRenderer.kt +++ /dev/null @@ -1,392 +0,0 @@ -package se.ansman.kotshi.renderer - -import com.squareup.kotlinpoet.* -import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.plusParameter -import se.ansman.kotshi.* -import se.ansman.kotshi.Types.Moshi.jsonDataException -import se.ansman.kotshi.Types.Moshi.jsonReaderToken -import se.ansman.kotshi.model.* - -class LegacyDataClassAdapterRenderer( - private val adapter: DataClassJsonAdapter, - private val createAnnotationsUsingConstructor: Boolean -) : AdapterRenderer(adapter) { - private val adapters = adapter.properties.generateAdapterProperties() - private val propertyNames = adapter.properties.mapTo(mutableSetOf()) { it.jsonName } - private val parentLabels = adapter.polymorphicLabels.filterKeys { it !in propertyNames } - private val serializedNames = adapter.serializedProperties.map { it.jsonName }.toSet() - private val optionsProperty = jsonOptionsProperty(serializedNames + parentLabels.keys) - - override fun TypeSpec.Builder.renderSetup() { - primaryConstructor( - FunSpec.constructorBuilder() - .applyIf(adapters.isNotEmpty()) { addParameter(moshiParameterName, Types.Moshi.moshi) } - .applyIf(adapters.any { it.key.type.hasTypeVariable }) { - addParameter(typesParameterName, Types.Kotshi.typesArray) - } - .build() - ) - addProperties(adapters.values) - addProperty(optionsProperty) - } - - private fun Iterable.generateAdapterProperties(): Map = - asSequence() - .filter { it.shouldUseAdapter } - .associateWith { property -> - PropertySpec - .builder( - nameAllocator.newName(property.suggestedAdapterName), - Types.Moshi.jsonAdapter.plusParameter(property.type), - KModifier.PRIVATE - ) - .initializer( - CodeBlock.builder() - .add("«moshi.adapter(") - .add(property.asRuntimeType { typeVariableName -> - val genericIndex = - adapter.targetTypeVariables.indexOfFirst { it.name == typeVariableName.name } - require(genericIndex >= 0) { - throw IllegalStateException("Element ${adapter.targetType} is generic but is of an unknown type") - } - CodeBlock.of("types[$genericIndex]") - }) - .add(", %L", property.annotations(createAnnotationsUsingConstructor)) - .add(", %S", property.name) - .add(")»") - .build() - ) - .build() - } - - override fun FunSpec.Builder.renderToJson( - writerParameter: ParameterSpec, - valueParameter: ParameterSpec - ) { - fun addBody(): FunSpec.Builder = - addStatement("%N.beginObject()", writerParameter) - .applyEach(parentLabels.entries) { (key, value) -> - addStatement("%N.name(%S).value(%S)", writerParameter, key, value) - } - .applyEach(adapter.serializedProperties) { property -> - addStatement("%N.name(%S)", writerParameter, property.jsonName) - val getter = CodeBlock.of("%N.%N", valueParameter, property.name) - - if (property.shouldUseAdapter) { - addCode( - "%N.toJson(%N, ", adapters.getValue(property), - writerParameter - ) - .addCode(getter) - .addCode(")\n") - } else when (property.type.notNull()) { - STRING, - INT, - LONG, - FLOAT, - DOUBLE, - SHORT, - BOOLEAN -> addStatement("%N.value(%L)", writerParameter, getter) - - BYTE -> addStatement("%N.%M(%L)", writerParameter, Functions.Kotshi.byteValue, getter) - CHAR -> addStatement("%N.%M(%L)", writerParameter, Functions.Kotshi.value, getter) - else -> error("Property ${property.name} is not primitive ${property.type} but requested non adapter use") - } - } - .addStatement("%N.endObject()", writerParameter) - - addIf("%N == null", valueParameter) { - addStatement("%N.nullValue()", writerParameter) - addStatement("return") - } - - val serializeNullsEnabled = when (adapter.serializeNulls) { - SerializeNulls.DEFAULT -> null - SerializeNulls.ENABLED -> true - SerializeNulls.DISABLED -> false - } - if (serializeNullsEnabled != null) { - this - .addStatement("val·serializeNulls·= %N.serializeNulls", writerParameter) - .addStatement("%N.serializeNulls·= %L", writerParameter, serializeNullsEnabled) - .beginControlFlow("try") - .apply { addBody() } - .nextControlFlow("finally") - .addStatement("%N.serializeNulls·= serializeNulls", writerParameter) - .endControlFlow() - } else { - addBody() - } - } - - override fun FunSpec.Builder.renderFromJson(readerParameter: ParameterSpec) { - val variables = adapter.serializedProperties.associateBy({ it }, { it.createVariables() }) - this - .addStatement( - "if (%N.peek() == %T.NULL) return %N.nextNull()", - readerParameter, - jsonReaderToken, - readerParameter - ) - .addCode("\n") - .applyEach(variables.values) { variable -> - addCode("%L", variable.value) - if (variable.helper != null) { - addCode("%L", variable.helper) - } - } - .addCode("\n") - .addStatement("%N.beginObject()", readerParameter) - .addWhile("%N.hasNext()", readerParameter) { - addWhen("%N.selectName(%N)", readerParameter, optionsProperty) { - variables.entries.forEachIndexed { index, (property, variable) -> - addWhenBranch("%L", index) { - if (property.shouldUseAdapter) { - addStatement( - "%N·= %N.fromJson(%N)", - variable.value, - adapters.getValue(property), - readerParameter - ) - if (variable.helper != null) { - if (property.type.isNullable) { - addStatement("%N·= true", variable.helper) - } else { - addStatement("?.also { %N = true }", variable.helper) - } - } - } else { - fun FunSpec.Builder.readPrimitive(functionName: String, vararg args: Any) { - addIfElse( - "%N.peek() == %T.NULL", - readerParameter, jsonReaderToken - ) { - addStatement("%N.skipValue()", readerParameter) - } - addElse { - addStatement( - "%N·= %N.$functionName()", - variable.value, - readerParameter, - *args - ) - if (variable.helper != null && !property.type.isNullable) { - addStatement("%N·= true", variable.helper) - } - } - if (variable.helper != null && property.type.isNullable) { - addStatement("%N·= true", variable.helper) - } - } - - when (property.type.notNull()) { - STRING -> readPrimitive("nextString") - BOOLEAN -> readPrimitive("nextBoolean") - BYTE -> readPrimitive("%M", Functions.Kotshi.nextByte) - SHORT -> readPrimitive("%M", Functions.Kotshi.nextShort) - INT -> readPrimitive("nextInt") - LONG -> readPrimitive("nextLong") - CHAR -> readPrimitive("%M", Functions.Kotshi.nextChar) - FLOAT -> readPrimitive("%M", Functions.Kotshi.nextFloat) - DOUBLE -> readPrimitive("nextDouble") - else -> error( - "Internal Kotshi error when processing ${adapter.targetType}. " + - "Expected property type to be a primitive but was ${property.type}. " + - "Please open an issue here: https://github.com/ansman/kotshi/issues/new" - ) - } - } - } - } - parentLabels.keys.forEachIndexed { i, name -> - addWhenBranch("%L", variables.size + i) { - addComment("Consumes the '%L' label key from the parent", name) - addStatement("%N.nextString()", readerParameter) - } - } - addWhenBranch("-1") { - addStatement("%N.skipName()", readerParameter) - addStatement("%N.skipValue()", readerParameter) - } - } - } - .addStatement("%N.endObject()", readerParameter) - .addCode("\n") - .apply { - val propertiesToCheck = variables.entries - .filter { (property, _) -> - !property.type.isNullable && !property.hasDefaultValue - } - - if (propertiesToCheck.isNotEmpty()) { - val stringBuilder = PropertySpec - .builder("errorBuilder", StringBuilder::class.asTypeName().nullable()) - .mutable() - .initializer("null") - .build() - addCode("%L", stringBuilder) - for ((property, variable) in propertiesToCheck) { - addIf("%L", variable.isNotSet) { - if (property.name == property.jsonName) { - addStatement( - "%N = %N.%M(%S)", - stringBuilder, - stringBuilder, - Functions.Kotshi.appendNullableError, - property.name - ) - } else { - addStatement( - "%N = %N.%M(%S, %S)", - stringBuilder, - stringBuilder, - Functions.Kotshi.appendNullableError, - property.name, - property.jsonName - ) - } - } - } - - addIf("%N != null", stringBuilder) { - addStatement( - "%N.append(\" (at path \").append(%N.path).append(')')", - stringBuilder, - readerParameter - ) - addStatement("throw %T(%N.toString())", jsonDataException, stringBuilder) - } - addCode("\n") - } - } - .apply { - val constructorProperties = adapter.properties.filter { !it.hasDefaultValue } - val copyProperties = adapter.serializedProperties.filter { it.hasDefaultValue } - - addCode("return·%T(«", adapter.targetType) - constructorProperties.forEachIndexed { index, property -> - val variable = variables.getValue(property) - - if (index > 0) { - addCode(",") - } - addCode("\n%N·= %N", property.name, variable.value.name) - if (variable.value.type.isNullable && !property.type.isNullable) { - addCode("!!") - } - } - addCode("»") - if (constructorProperties.isNotEmpty()) addCode("\n") - addCode(")") - if (copyProperties.isNotEmpty()) { - addControlFlow(".let") { - addCode("it.copy(«") - copyProperties.forEachIndexed { index, property -> - val variable = variables.getValue(property) - - if (index > 0) { - addCode(",") - } - addCode("\n%N = ", property.name) - - if (variable.helper == null) { - addCode("%N ?: it.%N", variable.value, property.name) - } else { - addCode("if (%L) %N else it.%N", variable.isSet, variable.value, property.name) - } - } - addCode("»\n)\n") - } - } - } - } - - private fun Property.createVariables() = - LegacyPropertyVariables( - value = PropertySpec - .builder( - name = nameAllocator.newName(name), - type = if (type.isPrimitive && !shouldUseAdapter) type else type.nullable() - ) - .initializer( - when { - type.isPrimitive && !shouldUseAdapter -> - when (type) { - BOOLEAN -> CodeBlock.of("false") - BYTE -> CodeBlock.of("0") - SHORT -> CodeBlock.of("0") - INT -> CodeBlock.of("0") - LONG -> CodeBlock.of("0L") - CHAR -> CodeBlock.of("'\\u0000'") - FLOAT -> CodeBlock.of("0f") - DOUBLE -> CodeBlock.of("0.0") - else -> throw AssertionError() - } - - else -> CodeBlock.of("null") - } - ) - .mutable() - .build(), - helper = if (type.isPrimitive && !shouldUseAdapter || type.isNullable && hasDefaultValue) { - PropertySpec - .builder(nameAllocator.newName("${name}IsSet"), BOOLEAN) - .mutable() - .initializer("false") - .build() - } else { - null - } - ) - -} - -private data class LegacyPropertyVariables( - val value: PropertySpec, - val helper: PropertySpec? -) { - val isNotSet: CodeBlock by lazy(LazyThreadSafetyMode.NONE) { - if (helper == null) { - CodeBlock.of("%N == null", value) - } else { - CodeBlock.of("!%N", helper) - } - } - val isSet: CodeBlock by lazy(LazyThreadSafetyMode.NONE) { - if (helper == null) { - CodeBlock.of("%N != null", value) - } else { - CodeBlock.of("%N", helper) - } - } - -} - -private fun Property.annotations(createAnnotationsUsingConstructor: Boolean): CodeBlock = - CodeBlock.builder() - .add("%M(", Functions.Kotlin.setOf) - .applyIf(jsonQualifiers.size > 1) { - indent() - add("\n") - } - .applyEachIndexed(jsonQualifiers.sortedBy { it.annotationName }) { index, qualifier -> - if (index > 0) add(",\n") - add(qualifier.render(createAnnotationsUsingConstructor)) - } - .applyIf(jsonQualifiers.size > 1) { - unindent() - add("\n") - } - .add(")") - .build() - - -private val TypeName.hasTypeVariable: Boolean - get() = when (this) { - is ClassName -> false - Dynamic -> false - is LambdaTypeName -> receiver?.hasTypeVariable ?: false || parameters.any { it.type.hasTypeVariable } || returnType.hasTypeVariable - is ParameterizedTypeName -> typeArguments.any { it.hasTypeVariable } - is TypeVariableName -> true - is WildcardTypeName -> inTypes.any { it.hasTypeVariable } || outTypes.any { it.hasTypeVariable } - } diff --git a/tests/ksp/build.gradle.kts b/tests/ksp/build.gradle.kts index d2048d3..e9e0b5b 100644 --- a/tests/ksp/build.gradle.kts +++ b/tests/ksp/build.gradle.kts @@ -1,5 +1,3 @@ -import com.google.devtools.ksp.gradle.KspExtension - plugins { id("test-library") alias(libs.plugins.ksp) @@ -17,16 +15,4 @@ kotlin { dependencies { ksp(projects.compiler) testImplementation(libs.compileTesting.ksp) -} - -fun KspExtension.argFromGradleProperty(name: String) { - val value = providers.gradleProperty(name).orNull - if (value != null) { - arg(name, value) - } -} - -ksp { - argFromGradleProperty("kotshi.createAnnotationsUsingConstructor") - argFromGradleProperty("kotshi.useLegacyDataClassRenderer") } \ No newline at end of file