Upgrade to Kotlin 1.6 + always instantiate annotations (#1425)

This commit is contained in:
Zac Sweers
2021-11-17 12:46:47 -05:00
committed by GitHub
parent 9440e5c7d0
commit 73553286fb
15 changed files with 96 additions and 383 deletions

View File

@@ -10,19 +10,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
kotlin-version: [ '1.5.31', '1.6.0-RC' ]
# TODO add back KSP 1.6.0-1.0.1 once it's out
ksp-version: [ '1.5.31-1.0.1' ]
kotlin-test-mode: [ 'REFLECT', 'KSP', 'KAPT' ] kotlin-test-mode: [ 'REFLECT', 'KSP', 'KAPT' ]
exclude:
# - kotlin-version: '1.5.31'
# ksp-version: '1.6.0-RC-1.0.1-RC'
- kotlin-version: '1.6.0-RC'
ksp-version: '1.5.31-1.0.1'
env:
MOSHI_KOTLIN_VERSION: '${{ matrix.kotlin-version }}'
MOSHI_KSP_VERSION: '${{ matrix.ksp-version }}'
steps: steps:
- name: Checkout - name: Checkout
@@ -48,10 +36,10 @@ jobs:
java-version: '17' java-version: '17'
- name: Test - name: Test
run: ./gradlew build check --stacktrace -PkotlinTestMode=${{ matrix.kotlin-test-mode }} -PkotlinVersion=${{ matrix.kotlin-version }} run: ./gradlew build check --stacktrace -PkotlinTestMode=${{ matrix.kotlin-test-mode }}
- name: Publish (default branch only) - name: Publish (default branch only)
if: github.repository == 'square/moshi' && github.ref == 'refs/heads/master' && matrix.kotlin-version == '1.5.31' && matrix.kotlin-test-mode == 'reflect' if: github.repository == 'square/moshi' && github.ref == 'refs/heads/master' && matrix.kotlin-test-mode == 'reflect'
run: ./gradlew publish run: ./gradlew publish
env: env:
ORG_GRADLE_PROJECT_mavenCentralUsername: '${{ secrets.SONATYPE_NEXUS_USERNAME }}' ORG_GRADLE_PROJECT_mavenCentralUsername: '${{ secrets.SONATYPE_NEXUS_USERNAME }}'

View File

@@ -14,34 +14,15 @@
# limitations under the License. # limitations under the License.
# #
# For Dokka https://github.com/Kotlin/dokka/issues/1405 # Memory for Dokka https://github.com/Kotlin/dokka/issues/1405
# --add-opens for GJF https://github.com/google/google-java-format#jdk-16
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 \ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 \
--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ --add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ --add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ --add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ --add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
kotlin.daemon.jvmargs=-Dfile.encoding=UTF-8 \
--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
# Kapt doesn't read the above jvm args when workers are used
# https://youtrack.jetbrains.com/issue/KT-48402
kapt.use.worker.api=false
kapt.include.compile.classpath=false kapt.include.compile.classpath=false
GROUP=com.squareup.moshi GROUP=com.squareup.moshi

View File

@@ -16,14 +16,14 @@
autoService = "1.0" autoService = "1.0"
gjf = "1.11.0" gjf = "1.11.0"
jvmTarget = "1.8" jvmTarget = "1.8"
kotlin = "1.5.31" kotlin = "1.6.0"
kotlinCompileTesting = "1.4.4" kotlinCompileTesting = "1.4.4"
kotlinpoet = "1.10.2" kotlinpoet = "1.10.2"
ksp = "1.5.31-1.0.1" ksp = "1.6.0-1.0.1"
ktlint = "0.41.0" ktlint = "0.41.0"
[plugins] [plugins]
dokka = { id = "org.jetbrains.dokka", version = "1.5.30" } dokka = { id = "org.jetbrains.dokka", version = "1.5.31" }
japicmp = { id = "me.champeau.gradle.japicmp", version = "0.2.9" } japicmp = { id = "me.champeau.gradle.japicmp", version = "0.2.9" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.17.0" } mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.17.0" }

View File

@@ -37,6 +37,7 @@ tasks.withType<KotlinCompile>().configureEach {
} }
} }
// --add-opens for kapt to work. KGP covers this for us but local JVMs in tests do not
tasks.withType<Test>().configureEach { tasks.withType<Test>().configureEach {
jvmArgs( jvmArgs(
"--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",

View File

@@ -186,32 +186,14 @@ public class AdapterGenerator(
result.addAnnotation(COMMON_SUPPRESS) result.addAnnotation(COMMON_SUPPRESS)
result.addType(generatedAdapter) result.addType(generatedAdapter)
val proguardConfig = if (generateProguardRules) { val proguardConfig = if (generateProguardRules) {
generatedAdapter.createProguardRule(target.instantiateAnnotations) generatedAdapter.createProguardRule()
} else { } else {
null null
} }
return PreparedAdapter(result.build(), proguardConfig) return PreparedAdapter(result.build(), proguardConfig)
} }
private fun TypeSpec.createProguardRule(instantiateAnnotations: Boolean): ProguardConfig { private fun TypeSpec.createProguardRule(): ProguardConfig {
val adapterProperties = if (instantiateAnnotations) {
// Don't need to do anything special if we instantiate them directly!
emptySet()
} else {
propertySpecs
.asSequence()
.filter { prop ->
prop.type.rawType() == JsonAdapter::class.asClassName()
}
.filter { prop -> prop.annotations.isNotEmpty() }
.mapTo(mutableSetOf()) { prop ->
QualifierAdapterProperty(
name = prop.name,
qualifiers = prop.annotations.mapTo(mutableSetOf()) { it.typeName.rawType() }
)
}
}
val adapterConstructorParams = when (requireNotNull(primaryConstructor).parameters.size) { val adapterConstructorParams = when (requireNotNull(primaryConstructor).parameters.size) {
1 -> listOf(CN_MOSHI.reflectionName()) 1 -> listOf(CN_MOSHI.reflectionName())
2 -> listOf(CN_MOSHI.reflectionName(), "${CN_TYPE.reflectionName()}[]") 2 -> listOf(CN_MOSHI.reflectionName(), "${CN_TYPE.reflectionName()}[]")
@@ -237,7 +219,6 @@ public class AdapterGenerator(
adapterConstructorParams = adapterConstructorParams, adapterConstructorParams = adapterConstructorParams,
targetConstructorHasDefaults = hasDefaultProperties, targetConstructorHasDefaults = hasDefaultProperties,
targetConstructorParams = parameterTypes, targetConstructorParams = parameterTypes,
qualifierProperties = adapterProperties
) )
} }

View File

@@ -29,10 +29,8 @@ import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeVariableName import com.squareup.kotlinpoet.TypeVariableName
import com.squareup.kotlinpoet.WildcardTypeName import com.squareup.kotlinpoet.WildcardTypeName
import com.squareup.kotlinpoet.asClassName import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.joinToCode import com.squareup.kotlinpoet.joinToCode
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Types
import java.util.Locale import java.util.Locale
/** A JsonAdapter that can be used to encode and decode a particular field. */ /** A JsonAdapter that can be used to encode and decode a particular field. */
@@ -40,7 +38,6 @@ import java.util.Locale
public data class DelegateKey( public data class DelegateKey(
private val type: TypeName, private val type: TypeName,
private val jsonQualifiers: List<AnnotationSpec>, private val jsonQualifiers: List<AnnotationSpec>,
private val instantiateAnnotations: Boolean
) { ) {
public val nullable: Boolean get() = type.isNullable public val nullable: Boolean get() = type.isNullable
@@ -67,22 +64,13 @@ public data class DelegateKey(
val (initializerString, args) = when { val (initializerString, args) = when {
jsonQualifiers.isEmpty() -> ", %M()" to arrayOf(MemberName("kotlin.collections", "emptySet")) jsonQualifiers.isEmpty() -> ", %M()" to arrayOf(MemberName("kotlin.collections", "emptySet"))
instantiateAnnotations -> {
", setOf(%L)" to arrayOf(jsonQualifiers.map { it.asInstantiationExpression() }.joinToCode())
}
else -> { else -> {
", %T.getFieldJsonQualifierAnnotations(javaClass, " + ", setOf(%L)" to arrayOf(jsonQualifiers.map { it.asInstantiationExpression() }.joinToCode())
"%S)" to arrayOf(Types::class.asTypeName(), adapterName)
} }
} }
val finalArgs = arrayOf(*standardArgs, *args, propertyName) val finalArgs = arrayOf(*standardArgs, *args, propertyName)
return PropertySpec.builder(adapterName, adapterTypeName, KModifier.PRIVATE) return PropertySpec.builder(adapterName, adapterTypeName, KModifier.PRIVATE)
.apply {
if (!instantiateAnnotations) {
addAnnotations(jsonQualifiers)
}
}
.initializer("%N.adapter(%L$initializerString, %S)", *finalArgs) .initializer("%N.adapter(%L$initializerString, %S)", *finalArgs)
.build() .build()
} }

View File

@@ -39,7 +39,6 @@ public data class ProguardConfig(
val adapterConstructorParams: List<String>, val adapterConstructorParams: List<String>,
val targetConstructorHasDefaults: Boolean, val targetConstructorHasDefaults: Boolean,
val targetConstructorParams: List<String>, val targetConstructorParams: List<String>,
val qualifierProperties: Set<QualifierAdapterProperty>
) { ) {
public fun outputFilePathWithoutExtension(canonicalName: String): String { public fun outputFilePathWithoutExtension(canonicalName: String): String {
return "META-INF/proguard/moshi-$canonicalName" return "META-INF/proguard/moshi-$canonicalName"
@@ -66,21 +65,8 @@ public data class ProguardConfig(
// Keep the constructor for Moshi's reflective lookup // Keep the constructor for Moshi's reflective lookup
val constructorArgs = adapterConstructorParams.joinToString(",") val constructorArgs = adapterConstructorParams.joinToString(",")
appendLine(" public <init>($constructorArgs);") appendLine(" public <init>($constructorArgs);")
// Keep any qualifier properties
for (qualifierProperty in qualifierProperties) {
appendLine(" private com.squareup.moshi.JsonAdapter ${qualifierProperty.name};")
}
appendLine("}") appendLine("}")
qualifierProperties.asSequence()
.flatMap { it.qualifiers.asSequence() }
.map(ClassName::reflectionName)
.sorted()
.forEach { qualifier ->
appendLine("-if class $targetName")
appendLine("-keep @interface $qualifier")
}
if (targetConstructorHasDefaults) { if (targetConstructorHasDefaults) {
// If the target class has default parameter values, keep its synthetic constructor // If the target class has default parameter values, keep its synthetic constructor
// //

View File

@@ -28,7 +28,6 @@ public data class TargetType(
val typeVariables: List<TypeVariableName>, val typeVariables: List<TypeVariableName>,
val isDataClass: Boolean, val isDataClass: Boolean,
val visibility: KModifier, val visibility: KModifier,
val instantiateAnnotations: Boolean
) { ) {
init { init {

View File

@@ -144,12 +144,11 @@ public class JsonClassCodegenProcessor : AbstractProcessor() {
types, types,
element, element,
cachedClassInspector, cachedClassInspector,
instantiateAnnotations
) ?: return null ) ?: return null
val properties = mutableMapOf<String, PropertyGenerator>() val properties = mutableMapOf<String, PropertyGenerator>()
for (property in type.properties.values) { for (property in type.properties.values) {
val generator = property.generator(messager, element, elements, type.instantiateAnnotations) val generator = property.generator(messager, element, elements)
if (generator != null) { if (generator != null) {
properties[property.name] = generator properties[property.name] = generator
} }

View File

@@ -47,10 +47,8 @@ import com.squareup.moshi.kotlin.codegen.api.rawType
import com.squareup.moshi.kotlin.codegen.api.unwrapTypeAlias import com.squareup.moshi.kotlin.codegen.api.unwrapTypeAlias
import kotlinx.metadata.KmConstructor import kotlinx.metadata.KmConstructor
import kotlinx.metadata.jvm.signature import kotlinx.metadata.jvm.signature
import java.lang.annotation.ElementType
import java.lang.annotation.Retention import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
import javax.annotation.processing.Messager import javax.annotation.processing.Messager
import javax.lang.model.element.AnnotationMirror import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.Element import javax.lang.model.element.Element
@@ -72,7 +70,6 @@ private val VISIBILITY_MODIFIERS = setOf(
KModifier.PROTECTED, KModifier.PROTECTED,
KModifier.PUBLIC KModifier.PUBLIC
) )
private val ANNOTATION_INSTANTIATION_MIN_VERSION = KotlinVersion(1, 6, 0)
private fun Collection<KModifier>.visibility(): KModifier { private fun Collection<KModifier>.visibility(): KModifier {
return find { it in VISIBILITY_MODIFIERS } ?: KModifier.PUBLIC return find { it in VISIBILITY_MODIFIERS } ?: KModifier.PUBLIC
@@ -126,7 +123,6 @@ internal fun targetType(
types: Types, types: Types,
element: TypeElement, element: TypeElement,
cachedClassInspector: MoshiCachedClassInspector, cachedClassInspector: MoshiCachedClassInspector,
instantiateAnnotationsEnabled: Boolean
): TargetType? { ): TargetType? {
val typeMetadata = element.getAnnotation(Metadata::class.java) val typeMetadata = element.getAnnotation(Metadata::class.java)
if (typeMetadata == null) { if (typeMetadata == null) {
@@ -208,11 +204,6 @@ internal fun targetType(
} }
} }
val instantiateAnnotations = instantiateAnnotationsEnabled && run {
val (major, minor, patch) = typeMetadata.metadataVersion
val languageVersion = KotlinVersion(major, minor, patch)
languageVersion >= ANNOTATION_INSTANTIATION_MIN_VERSION
}
val kotlinApi = cachedClassInspector.toTypeSpec(kmClass) val kotlinApi = cachedClassInspector.toTypeSpec(kmClass)
val typeVariables = kotlinApi.typeVariables val typeVariables = kotlinApi.typeVariables
val appliedType = AppliedType.get(element) val appliedType = AppliedType.get(element)
@@ -329,7 +320,6 @@ internal fun targetType(
typeVariables = typeVariables, typeVariables = typeVariables,
isDataClass = KModifier.DATA in kotlinApi.modifiers, isDataClass = KModifier.DATA in kotlinApi.modifiers,
visibility = resolvedVisibility, visibility = resolvedVisibility,
instantiateAnnotations = instantiateAnnotations
) )
} }
@@ -431,7 +421,6 @@ internal fun TargetProperty.generator(
messager: Messager, messager: Messager,
sourceElement: TypeElement, sourceElement: TypeElement,
elements: Elements, elements: Elements,
instantiateAnnotations: Boolean
): PropertyGenerator? { ): PropertyGenerator? {
if (jsonIgnore) { if (jsonIgnore) {
if (!hasDefault) { if (!hasDefault) {
@@ -442,7 +431,7 @@ internal fun TargetProperty.generator(
) )
return null return null
} }
return PropertyGenerator(this, DelegateKey(type, emptyList(), instantiateAnnotations), true) return PropertyGenerator(this, DelegateKey(type, emptyList()), true)
} }
if (!isVisible) { if (!isVisible) {
@@ -474,16 +463,6 @@ internal fun TargetProperty.generator(
) )
} }
} }
if (!instantiateAnnotations) {
annotationElement.getAnnotation(Target::class.java)?.let {
if (ElementType.FIELD !in it.value) {
messager.printMessage(
ERROR,
"JsonQualifier @${qualifierRawType.simpleName} must support FIELD target"
)
}
}
}
} }
val jsonQualifierSpecs = qualifiers.map { val jsonQualifierSpecs = qualifiers.map {
@@ -494,7 +473,7 @@ internal fun TargetProperty.generator(
return PropertyGenerator( return PropertyGenerator(
this, this,
DelegateKey(type, jsonQualifierSpecs, instantiateAnnotations) DelegateKey(type, jsonQualifierSpecs)
) )
} }

View File

@@ -34,7 +34,6 @@ import com.squareup.moshi.JsonClass
import com.squareup.moshi.kotlin.codegen.api.AdapterGenerator import com.squareup.moshi.kotlin.codegen.api.AdapterGenerator
import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATED import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATED
import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATE_PROGUARD_RULES import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATE_PROGUARD_RULES
import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_INSTANTIATE_ANNOTATIONS
import com.squareup.moshi.kotlin.codegen.api.Options.POSSIBLE_GENERATED_NAMES import com.squareup.moshi.kotlin.codegen.api.Options.POSSIBLE_GENERATED_NAMES
import com.squareup.moshi.kotlin.codegen.api.ProguardConfig import com.squareup.moshi.kotlin.codegen.api.ProguardConfig
import com.squareup.moshi.kotlin.codegen.api.PropertyGenerator import com.squareup.moshi.kotlin.codegen.api.PropertyGenerator
@@ -64,8 +63,6 @@ private class JsonClassSymbolProcessor(
} }
} }
private val generateProguardRules = environment.options[OPTION_GENERATE_PROGUARD_RULES]?.toBooleanStrictOrNull() ?: true private val generateProguardRules = environment.options[OPTION_GENERATE_PROGUARD_RULES]?.toBooleanStrictOrNull() ?: true
private val instantiateAnnotations = (environment.options[OPTION_INSTANTIATE_ANNOTATIONS]?.toBooleanStrictOrNull() ?: true) &&
environment.kotlinVersion.isAtLeast(1, 6)
override fun process(resolver: Resolver): List<KSAnnotated> { override fun process(resolver: Resolver): List<KSAnnotated> {
val generatedAnnotation = generatedOption?.let { val generatedAnnotation = generatedOption?.let {
@@ -125,11 +122,11 @@ private class JsonClassSymbolProcessor(
resolver: Resolver, resolver: Resolver,
originalType: KSDeclaration, originalType: KSDeclaration,
): AdapterGenerator? { ): AdapterGenerator? {
val type = targetType(originalType, resolver, logger, instantiateAnnotations) ?: return null val type = targetType(originalType, resolver, logger) ?: return null
val properties = mutableMapOf<String, PropertyGenerator>() val properties = mutableMapOf<String, PropertyGenerator>()
for (property in type.properties.values) { for (property in type.properties.values) {
val generator = property.generator(logger, resolver, originalType, type.instantiateAnnotations) val generator = property.generator(logger, resolver, originalType)
if (generator != null) { if (generator != null) {
properties[property.name] = generator properties[property.name] = generator
} }

View File

@@ -43,7 +43,6 @@ internal fun TargetProperty.generator(
logger: KSPLogger, logger: KSPLogger,
resolver: Resolver, resolver: Resolver,
originalType: KSDeclaration, originalType: KSDeclaration,
instantiateAnnotations: Boolean
): PropertyGenerator? { ): PropertyGenerator? {
if (jsonIgnore) { if (jsonIgnore) {
if (!hasDefault) { if (!hasDefault) {
@@ -53,7 +52,7 @@ internal fun TargetProperty.generator(
) )
return null return null
} }
return PropertyGenerator(this, DelegateKey(type, emptyList(), instantiateAnnotations), true) return PropertyGenerator(this, DelegateKey(type, emptyList()), true)
} }
if (!isVisible) { if (!isVisible) {
@@ -81,15 +80,6 @@ internal fun TargetProperty.generator(
) )
} }
} }
if (!instantiateAnnotations) {
annotationElement.findAnnotationWithType<Target>()?.let {
if (AnnotationTarget.FIELD !in it.allowedTargets) {
logger.error(
"JsonQualifier @${qualifierRawType.simpleName} must support FIELD target"
)
}
}
}
} }
} }
@@ -101,7 +91,7 @@ internal fun TargetProperty.generator(
return PropertyGenerator( return PropertyGenerator(
this, this,
DelegateKey(type, jsonQualifierSpecs, instantiateAnnotations) DelegateKey(type, jsonQualifierSpecs)
) )
} }

View File

@@ -58,7 +58,6 @@ internal fun targetType(
type: KSDeclaration, type: KSDeclaration,
resolver: Resolver, resolver: Resolver,
logger: KSPLogger, logger: KSPLogger,
instantiateAnnotations: Boolean
): TargetType? { ): TargetType? {
if (type !is KSClassDeclaration) { if (type !is KSClassDeclaration) {
logger.error("@JsonClass can't be applied to ${type.qualifiedName?.asString()}: must be a Kotlin class", type) logger.error("@JsonClass can't be applied to ${type.qualifiedName?.asString()}: must be a Kotlin class", type)
@@ -154,7 +153,6 @@ internal fun targetType(
typeVariables = typeVariables, typeVariables = typeVariables,
isDataClass = Modifier.DATA in type.modifiers, isDataClass = Modifier.DATA in type.modifiers,
visibility = resolvedVisibility, visibility = resolvedVisibility,
instantiateAnnotations = instantiateAnnotations
) )
} }

View File

@@ -20,7 +20,6 @@ import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonReader
import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATED import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATED
import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATE_PROGUARD_RULES import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATE_PROGUARD_RULES
import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_INSTANTIATE_ANNOTATIONS
import com.tschuchort.compiletesting.KotlinCompilation import com.tschuchort.compiletesting.KotlinCompilation
import com.tschuchort.compiletesting.SourceFile import com.tschuchort.compiletesting.SourceFile
import com.tschuchort.compiletesting.SourceFile.Companion.kotlin import com.tschuchort.compiletesting.SourceFile.Companion.kotlin
@@ -28,8 +27,6 @@ import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KClassifier import kotlin.reflect.KClassifier
import kotlin.reflect.KType import kotlin.reflect.KType
@@ -40,23 +37,7 @@ import kotlin.reflect.full.createType
import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.declaredMemberProperties
/** Execute kotlinc to confirm that either files are generated or errors are printed. */ /** Execute kotlinc to confirm that either files are generated or errors are printed. */
@RunWith(Parameterized::class) class JsonClassCodegenProcessorTest {
class JsonClassCodegenProcessorTest(
private val languageVersion: String,
private val instantiateAnnotations: Boolean
) {
companion object {
@JvmStatic
@Parameterized.Parameters(name = "languageVersion={0},instantiateAnnotations={1}")
fun data(): Collection<Array<Any>> {
return listOf(
arrayOf("1.5", true),
arrayOf("1.6", true),
arrayOf("1.6", false)
)
}
}
@Rule @JvmField var temporaryFolder: TemporaryFolder = TemporaryFolder() @Rule @JvmField var temporaryFolder: TemporaryFolder = TemporaryFolder()
@@ -494,13 +475,8 @@ class JsonClassCodegenProcessorTest(
""" """
) )
) )
if (languageVersion == "1.5" || !instantiateAnnotations) { // We instantiate directly so doesn't need to be FIELD
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
assertThat(result.messages).contains("JsonQualifier @UpperCase must support FIELD target")
} else {
// We instantiate directly!
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
}
} }
@Test @Test
@@ -710,32 +686,16 @@ class JsonClassCodegenProcessorTest(
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.UsingQualifiers" -> { "moshi-testPackage.UsingQualifiers" -> {
if (languageVersion == "1.5" || !instantiateAnnotations) { assertThat(generatedFile.readText()).contains(
assertThat(generatedFile.readText()).contains( """
""" -if class testPackage.UsingQualifiers
-if class testPackage.UsingQualifiers -keepnames class testPackage.UsingQualifiers
-keepnames class testPackage.UsingQualifiers -if class testPackage.UsingQualifiers
-if class testPackage.UsingQualifiers -keep class testPackage.UsingQualifiersJsonAdapter {
-keep class testPackage.UsingQualifiersJsonAdapter { public <init>(com.squareup.moshi.Moshi);
public <init>(com.squareup.moshi.Moshi); }
private com.squareup.moshi.JsonAdapter stringAtMyQualifierAdapter; """.trimIndent()
} )
-if class testPackage.UsingQualifiers
-keep @interface testPackage.MyQualifier
""".trimIndent()
)
} else {
assertThat(generatedFile.readText()).contains(
"""
-if class testPackage.UsingQualifiers
-keepnames class testPackage.UsingQualifiers
-if class testPackage.UsingQualifiers
-keep class testPackage.UsingQualifiersJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
}
""".trimIndent()
)
}
} }
"moshi-testPackage.MixedTypes" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.MixedTypes" -> assertThat(generatedFile.readText()).contains(
""" """
@@ -764,44 +724,22 @@ class JsonClassCodegenProcessorTest(
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.Complex" -> { "moshi-testPackage.Complex" -> {
if (languageVersion == "1.5" || !instantiateAnnotations) { assertThat(generatedFile.readText()).contains(
assertThat(generatedFile.readText()).contains( """
""" -if class testPackage.Complex
-if class testPackage.Complex -keepnames class testPackage.Complex
-keepnames class testPackage.Complex -if class testPackage.Complex
-if class testPackage.Complex -keep class testPackage.ComplexJsonAdapter {
-keep class testPackage.ComplexJsonAdapter { public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]); }
private com.squareup.moshi.JsonAdapter mutableListOfStringAtMyQualifierAdapter; -if class testPackage.Complex
} -keepnames class kotlin.jvm.internal.DefaultConstructorMarker
-if class testPackage.Complex -if class testPackage.Complex
-keep @interface testPackage.MyQualifier -keepclassmembers class testPackage.Complex {
-if class testPackage.Complex public synthetic <init>(java.lang.String,java.util.List,java.lang.Object,int,kotlin.jvm.internal.DefaultConstructorMarker);
-keepnames class kotlin.jvm.internal.DefaultConstructorMarker }
-if class testPackage.Complex """.trimIndent()
-keepclassmembers class testPackage.Complex { )
public synthetic <init>(java.lang.String,java.util.List,java.lang.Object,int,kotlin.jvm.internal.DefaultConstructorMarker);
}
""".trimIndent()
)
} else {
assertThat(generatedFile.readText()).contains(
"""
-if class testPackage.Complex
-keepnames class testPackage.Complex
-if class testPackage.Complex
-keep class testPackage.ComplexJsonAdapter {
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
}
-if class testPackage.Complex
-keepnames class kotlin.jvm.internal.DefaultConstructorMarker
-if class testPackage.Complex
-keepclassmembers class testPackage.Complex {
public synthetic <init>(java.lang.String,java.util.List,java.lang.Object,int,kotlin.jvm.internal.DefaultConstructorMarker);
}
""".trimIndent()
)
}
} }
"moshi-testPackage.MultipleMasks" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.MultipleMasks" -> assertThat(generatedFile.readText()).contains(
""" """
@@ -820,32 +758,16 @@ class JsonClassCodegenProcessorTest(
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.NestedType.NestedSimple" -> { "moshi-testPackage.NestedType.NestedSimple" -> {
if (languageVersion == "1.5" || !instantiateAnnotations) { assertThat(generatedFile.readText()).contains(
assertThat(generatedFile.readText()).contains( """
""" -if class testPackage.NestedType${'$'}NestedSimple
-if class testPackage.NestedType${'$'}NestedSimple -keepnames class testPackage.NestedType${'$'}NestedSimple
-keepnames class testPackage.NestedType${'$'}NestedSimple -if class testPackage.NestedType${'$'}NestedSimple
-if class testPackage.NestedType${'$'}NestedSimple -keep class testPackage.NestedType_NestedSimpleJsonAdapter {
-keep class testPackage.NestedType_NestedSimpleJsonAdapter { public <init>(com.squareup.moshi.Moshi);
public <init>(com.squareup.moshi.Moshi); }
private com.squareup.moshi.JsonAdapter stringAtNestedQualifierAdapter; """.trimIndent()
} )
-if class testPackage.NestedType${'$'}NestedSimple
-keep @interface testPackage.NestedType${'$'}NestedQualifier
""".trimIndent()
)
} else {
assertThat(generatedFile.readText()).contains(
"""
-if class testPackage.NestedType${'$'}NestedSimple
-keepnames class testPackage.NestedType${'$'}NestedSimple
-if class testPackage.NestedType${'$'}NestedSimple
-keep class testPackage.NestedType_NestedSimpleJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
}
""".trimIndent()
)
}
} }
else -> error("Unexpected proguard file! ${generatedFile.name}") else -> error("Unexpected proguard file! ${generatedFile.name}")
} }
@@ -860,8 +782,6 @@ class JsonClassCodegenProcessorTest(
inheritClassPath = true inheritClassPath = true
sources = sourceFiles.asList() sources = sourceFiles.asList()
verbose = false verbose = false
kotlincArguments = listOf("-language-version", languageVersion)
kaptArgs[OPTION_INSTANTIATE_ANNOTATIONS] = "$instantiateAnnotations"
} }
} }

View File

@@ -16,7 +16,6 @@
package com.squareup.moshi.kotlin.codegen.ksp package com.squareup.moshi.kotlin.codegen.ksp
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import com.squareup.moshi.kotlin.codegen.api.Options
import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATED import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATED
import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATE_PROGUARD_RULES import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATE_PROGUARD_RULES
import com.tschuchort.compiletesting.KotlinCompilation import com.tschuchort.compiletesting.KotlinCompilation
@@ -31,41 +30,9 @@ import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
/** Execute kotlinc to confirm that either files are generated or errors are printed. */ /** Execute kotlinc to confirm that either files are generated or errors are printed. */
@RunWith(Parameterized::class) class JsonClassSymbolProcessorTest {
class JsonClassSymbolProcessorTest(
private val languageVersion: String,
private val instantiateAnnotations: Boolean
) {
companion object {
@JvmStatic
@Parameterized.Parameters(name = "languageVersion={0},instantiateAnnotations={1}")
fun data(): Collection<Array<Any>> {
// TODO KSP doesn't recognize language version yet https://github.com/google/ksp/issues/611
// toe-holds for the future
val runtimeVersion = KotlinVersion.CURRENT
return mutableListOf<Array<Any>>().apply {
if (runtimeVersion.major != 1 || runtimeVersion.minor != 6) {
// True by default but still 1.5
// Can't test when running with 1.6 because lang version is still 1.6 per above comment
add(arrayOf("1.5", true))
}
// Redundant case, set to false but still 1.5
add(arrayOf("1.5", false))
if (runtimeVersion.major != 1 || runtimeVersion.minor != 5) {
// Happy case - 1.6 and true by default
// Can't test when running with 1.5 because lang version is still 1.5 per above comment
add(arrayOf("1.6", true))
}
// 1.6 but explicitly disabled
add(arrayOf("1.6", false))
}
}
}
@Rule @JvmField var temporaryFolder: TemporaryFolder = TemporaryFolder() @Rule @JvmField var temporaryFolder: TemporaryFolder = TemporaryFolder()
@@ -538,13 +505,8 @@ class JsonClassSymbolProcessorTest(
""" """
) )
) )
if (languageVersion == "1.5" || !instantiateAnnotations) { // We instantiate directly, no FIELD site target necessary
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
assertThat(result.messages).contains("JsonQualifier @UpperCase must support FIELD target")
} else {
// We instantiate directly!
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
}
} }
@Test @Test
@@ -758,32 +720,16 @@ class JsonClassSymbolProcessorTest(
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.UsingQualifiers" -> { "moshi-testPackage.UsingQualifiers" -> {
if (languageVersion == "1.5" || !instantiateAnnotations) { assertThat(generatedFile.readText()).contains(
assertThat(generatedFile.readText()).contains( """
""" -if class testPackage.UsingQualifiers
-if class testPackage.UsingQualifiers -keepnames class testPackage.UsingQualifiers
-keepnames class testPackage.UsingQualifiers -if class testPackage.UsingQualifiers
-if class testPackage.UsingQualifiers -keep class testPackage.UsingQualifiersJsonAdapter {
-keep class testPackage.UsingQualifiersJsonAdapter { public <init>(com.squareup.moshi.Moshi);
public <init>(com.squareup.moshi.Moshi); }
private com.squareup.moshi.JsonAdapter stringAtMyQualifierAdapter; """.trimIndent()
} )
-if class testPackage.UsingQualifiers
-keep @interface testPackage.MyQualifier
""".trimIndent()
)
} else {
assertThat(generatedFile.readText()).contains(
"""
-if class testPackage.UsingQualifiers
-keepnames class testPackage.UsingQualifiers
-if class testPackage.UsingQualifiers
-keep class testPackage.UsingQualifiersJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
}
""".trimIndent()
)
}
} }
"moshi-testPackage.MixedTypes" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.MixedTypes" -> assertThat(generatedFile.readText()).contains(
""" """
@@ -812,44 +758,22 @@ class JsonClassSymbolProcessorTest(
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.Complex" -> { "moshi-testPackage.Complex" -> {
if (languageVersion == "1.5" || !instantiateAnnotations) { assertThat(generatedFile.readText()).contains(
assertThat(generatedFile.readText()).contains( """
""" -if class testPackage.Complex
-if class testPackage.Complex -keepnames class testPackage.Complex
-keepnames class testPackage.Complex -if class testPackage.Complex
-if class testPackage.Complex -keep class testPackage.ComplexJsonAdapter {
-keep class testPackage.ComplexJsonAdapter { public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]); }
private com.squareup.moshi.JsonAdapter mutableListOfStringAtMyQualifierAdapter; -if class testPackage.Complex
} -keepnames class kotlin.jvm.internal.DefaultConstructorMarker
-if class testPackage.Complex -if class testPackage.Complex
-keep @interface testPackage.MyQualifier -keepclassmembers class testPackage.Complex {
-if class testPackage.Complex public synthetic <init>(java.lang.String,java.util.List,java.lang.Object,int,kotlin.jvm.internal.DefaultConstructorMarker);
-keepnames class kotlin.jvm.internal.DefaultConstructorMarker }
-if class testPackage.Complex """.trimIndent()
-keepclassmembers class testPackage.Complex { )
public synthetic <init>(java.lang.String,java.util.List,java.lang.Object,int,kotlin.jvm.internal.DefaultConstructorMarker);
}
""".trimIndent()
)
} else {
assertThat(generatedFile.readText()).contains(
"""
-if class testPackage.Complex
-keepnames class testPackage.Complex
-if class testPackage.Complex
-keep class testPackage.ComplexJsonAdapter {
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
}
-if class testPackage.Complex
-keepnames class kotlin.jvm.internal.DefaultConstructorMarker
-if class testPackage.Complex
-keepclassmembers class testPackage.Complex {
public synthetic <init>(java.lang.String,java.util.List,java.lang.Object,int,kotlin.jvm.internal.DefaultConstructorMarker);
}
""".trimIndent()
)
}
} }
"moshi-testPackage.MultipleMasks" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.MultipleMasks" -> assertThat(generatedFile.readText()).contains(
""" """
@@ -868,32 +792,16 @@ class JsonClassSymbolProcessorTest(
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.NestedType.NestedSimple" -> { "moshi-testPackage.NestedType.NestedSimple" -> {
if (languageVersion == "1.5" || !instantiateAnnotations) { assertThat(generatedFile.readText()).contains(
assertThat(generatedFile.readText()).contains( """
""" -if class testPackage.NestedType${'$'}NestedSimple
-if class testPackage.NestedType${'$'}NestedSimple -keepnames class testPackage.NestedType${'$'}NestedSimple
-keepnames class testPackage.NestedType${'$'}NestedSimple -if class testPackage.NestedType${'$'}NestedSimple
-if class testPackage.NestedType${'$'}NestedSimple -keep class testPackage.NestedType_NestedSimpleJsonAdapter {
-keep class testPackage.NestedType_NestedSimpleJsonAdapter { public <init>(com.squareup.moshi.Moshi);
public <init>(com.squareup.moshi.Moshi); }
private com.squareup.moshi.JsonAdapter stringAtNestedQualifierAdapter; """.trimIndent()
} )
-if class testPackage.NestedType${'$'}NestedSimple
-keep @interface testPackage.NestedType${'$'}NestedQualifier
""".trimIndent()
)
} else {
assertThat(generatedFile.readText()).contains(
"""
-if class testPackage.NestedType${'$'}NestedSimple
-keepnames class testPackage.NestedType${'$'}NestedSimple
-if class testPackage.NestedType${'$'}NestedSimple
-keep class testPackage.NestedType_NestedSimpleJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
}
""".trimIndent()
)
}
} }
else -> error("Unexpected proguard file! ${generatedFile.name}") else -> error("Unexpected proguard file! ${generatedFile.name}")
} }
@@ -909,8 +817,6 @@ class JsonClassSymbolProcessorTest(
sources = sourceFiles.asList() sources = sourceFiles.asList()
verbose = false verbose = false
kspIncremental = true // The default now kspIncremental = true // The default now
kotlincArguments = listOf("-language-version", languageVersion)
kspArgs[Options.OPTION_INSTANTIATE_ANNOTATIONS] = "$instantiateAnnotations"
} }
} }