Support direct annotation instantiation in code gen on Kotlin 1.6 (#1390)

This commit is contained in:
Zac Sweers
2021-10-22 13:43:09 -04:00
committed by GitHub
parent a9eaa849e5
commit b8fbe38118
17 changed files with 396 additions and 171 deletions

View File

@@ -4,13 +4,24 @@ on: [push, pull_request]
jobs: jobs:
build: build:
name: 'Java ${{ matrix.java-version }} | KSP ${{ matrix.use-ksp }}' name: 'Java ${{ matrix.java-version }} | Kotlin ${{ matrix.kotlin-version }} | KSP ${{ matrix.use-ksp }}'
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
use-ksp: [ true, false ] use-ksp: [ true, false ]
kotlin-version: [ '1.5.31', '1.6.0-RC' ]
ksp-version: [ '1.5.31-1.0.0', '1.6.0-RC-1.0.0' ]
exclude:
- kotlin-version: '1.5.31'
ksp-version: '1.6.0-RC-1.0.0'
- kotlin-version: '1.6.0-RC'
ksp-version: '1.5.31-1.0.0'
env:
MOSHI_KOTLIN_VERSION: '${{ matrix.kotlin-version }}'
MOSHI_KSP_VERSION: '${{ matrix.ksp-version }}'
steps: steps:
- name: Checkout - name: Checkout
@@ -36,8 +47,8 @@ jobs:
java-version: '17' java-version: '17'
- name: Test - name: Test
run: ./gradlew build check --stacktrace -PuseKsp=${{ matrix.use-ksp }} run: ./gradlew build check --stacktrace -PuseKsp=${{ matrix.use-ksp }} -PkotlinVersion=${{ matrix.kotlin-version }}
- name: Publish (default branch only) - name: Publish (default branch only)
if: github.repository == 'square/moshi' && github.ref == 'refs/heads/master' if: github.repository == 'square/moshi' && github.ref == 'refs/heads/master' && matrix.kotlin-version == '1.5.31' && matrix.use-ksp == 'false'
run: ./gradlew publish -PmavenCentralUsername=${{ secrets.SONATYPE_NEXUS_USERNAME }} -PmavenCentralPassword=${{ secrets.SONATYPE_NEXUS_PASSWORD }} --stacktrace run: ./gradlew publish -PmavenCentralUsername=${{ secrets.SONATYPE_NEXUS_USERNAME }} -PmavenCentralPassword=${{ secrets.SONATYPE_NEXUS_PASSWORD }} --stacktrace

View File

@@ -23,7 +23,12 @@ import java.net.URL
buildscript { buildscript {
dependencies { dependencies {
classpath(kotlin("gradle-plugin", version = libs.versions.kotlin.get())) val kotlinVersion = System.getenv("MOSHI_KOTLIN_VERSION")
?: libs.versions.kotlin.get()
val kspVersion = System.getenv("MOSHI_KSP_VERSION")
?: libs.versions.ksp.get()
classpath(kotlin("gradle-plugin", version = kotlinVersion))
classpath("com.google.devtools.ksp:symbol-processing-gradle-plugin:$kspVersion")
// https://github.com/melix/japicmp-gradle-plugin/issues/36 // https://github.com/melix/japicmp-gradle-plugin/issues/36
classpath("com.google.guava:guava:28.2-jre") classpath("com.google.guava:guava:28.2-jre")
} }
@@ -125,8 +130,9 @@ subprojects {
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
tasks.withType<KotlinCompile>().configureEach { tasks.withType<KotlinCompile>().configureEach {
kotlinOptions { kotlinOptions {
@Suppress("SuspiciousCollectionReassignment") // TODO re-enable when no longer supporting multiple kotlin versions
freeCompilerArgs += listOf("-progressive") // @Suppress("SuspiciousCollectionReassignment")
// freeCompilerArgs += listOf("-progressive")
jvmTarget = libs.versions.jvmTarget.get() jvmTarget = libs.versions.jvmTarget.get()
} }
} }

View File

@@ -18,7 +18,7 @@ gjf = "1.11.0"
jvmTarget = "1.8" jvmTarget = "1.8"
kotlin = "1.5.31" kotlin = "1.5.31"
kotlinCompileTesting = "1.4.4" kotlinCompileTesting = "1.4.4"
kotlinpoet = "1.10.1" kotlinpoet = "1.10.2"
ksp = "1.5.31-1.0.0" ksp = "1.5.31-1.0.0"
ktlint = "0.41.0" ktlint = "0.41.0"

View File

@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
kotlin("jvm") kotlin("jvm")
alias(libs.plugins.ksp) id("com.google.devtools.ksp")
id("com.vanniktech.maven.publish") id("com.vanniktech.maven.publish")
alias(libs.plugins.mavenShadow) alias(libs.plugins.mavenShadow)
} }

View File

@@ -185,26 +185,31 @@ internal 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() generatedAdapter.createProguardRule(target.instantiateAnnotations)
} else { } else {
null null
} }
return PreparedAdapter(result.build(), proguardConfig) return PreparedAdapter(result.build(), proguardConfig)
} }
private fun TypeSpec.createProguardRule(): ProguardConfig { private fun TypeSpec.createProguardRule(instantiateAnnotations: Boolean): ProguardConfig {
val adapterProperties = propertySpecs val adapterProperties = if (instantiateAnnotations) {
.asSequence() // Don't need to do anything special if we instantiate them directly!
.filter { prop -> emptySet()
prop.type.rawType() == JsonAdapter::class.asClassName() } else {
} propertySpecs
.filter { prop -> prop.annotations.isNotEmpty() } .asSequence()
.mapTo(mutableSetOf()) { prop -> .filter { prop ->
QualifierAdapterProperty( prop.type.rawType() == JsonAdapter::class.asClassName()
name = prop.name, }
qualifiers = prop.annotations.mapTo(mutableSetOf()) { it.typeName.rawType() } .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())

View File

@@ -17,6 +17,7 @@ package com.squareup.moshi.kotlin.codegen.api
import com.squareup.kotlinpoet.AnnotationSpec import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.MemberName import com.squareup.kotlinpoet.MemberName
import com.squareup.kotlinpoet.NameAllocator import com.squareup.kotlinpoet.NameAllocator
@@ -29,6 +30,7 @@ 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.asTypeName
import com.squareup.kotlinpoet.joinToCode
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Types import com.squareup.moshi.Types
import java.util.Locale import java.util.Locale
@@ -36,7 +38,8 @@ 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. */
internal data class DelegateKey( internal 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
) { ) {
val nullable get() = type.isNullable val nullable get() = type.isNullable
@@ -60,8 +63,12 @@ internal data class DelegateKey(
moshiParameter, moshiParameter,
typeRenderer.render(type) typeRenderer.render(type)
) )
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, " + ", %T.getFieldJsonQualifierAnnotations(javaClass, " +
"%S)" to arrayOf(Types::class.asTypeName(), adapterName) "%S)" to arrayOf(Types::class.asTypeName(), adapterName)
@@ -70,12 +77,25 @@ internal data class DelegateKey(
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)
.addAnnotations(jsonQualifiers) .apply {
if (!instantiateAnnotations) {
addAnnotations(jsonQualifiers)
}
}
.initializer("%N.adapter(%L$initializerString, %S)", *finalArgs) .initializer("%N.adapter(%L$initializerString, %S)", *finalArgs)
.build() .build()
} }
} }
private fun AnnotationSpec.asInstantiationExpression(): CodeBlock {
// <Type>(args)
return CodeBlock.of(
"%T(%L)",
typeName,
members.joinToCode()
)
}
/** /**
* Returns a suggested variable name derived from a list of type names. This just concatenates, * Returns a suggested variable name derived from a list of type names. This just concatenates,
* yielding types like MapOfStringLong. * yielding types like MapOfStringLong.

View File

@@ -36,6 +36,14 @@ internal object Options {
*/ */
const val OPTION_GENERATE_PROGUARD_RULES: String = "moshi.generateProguardRules" const val OPTION_GENERATE_PROGUARD_RULES: String = "moshi.generateProguardRules"
/**
* This boolean processing option controls whether or not Moshi will directly instantiate
* JsonQualifier annotations in Kotlin 1.6+. Note that this is enabled by default in Kotlin 1.6
* but can be disabled to restore the legacy behavior of storing annotations on generated adapter
* fields and looking them up reflectively.
*/
const val OPTION_INSTANTIATE_ANNOTATIONS: String = "moshi.instantiateAnnotations"
val POSSIBLE_GENERATED_NAMES = arrayOf( val POSSIBLE_GENERATED_NAMES = arrayOf(
ClassName("javax.annotation.processing", "Generated"), ClassName("javax.annotation.processing", "Generated"),
ClassName("javax.annotation", "Generated") ClassName("javax.annotation", "Generated")

View File

@@ -26,7 +26,8 @@ internal data class TargetType(
val properties: Map<String, TargetProperty>, val properties: Map<String, TargetProperty>,
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

@@ -23,6 +23,7 @@ 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
@@ -59,6 +60,7 @@ public class JsonClassCodegenProcessor : AbstractProcessor() {
private val annotation = JsonClass::class.java private val annotation = JsonClass::class.java
private var generatedType: ClassName? = null private var generatedType: ClassName? = null
private var generateProguardRules: Boolean = true private var generateProguardRules: Boolean = true
private var instantiateAnnotations: Boolean = true
override fun getSupportedAnnotationTypes(): Set<String> = setOf(annotation.canonicalName) override fun getSupportedAnnotationTypes(): Set<String> = setOf(annotation.canonicalName)
@@ -76,6 +78,7 @@ public class JsonClassCodegenProcessor : AbstractProcessor() {
} }
generateProguardRules = processingEnv.options[OPTION_GENERATE_PROGUARD_RULES]?.toBooleanStrictOrNull() ?: true generateProguardRules = processingEnv.options[OPTION_GENERATE_PROGUARD_RULES]?.toBooleanStrictOrNull() ?: true
instantiateAnnotations = processingEnv.options[OPTION_INSTANTIATE_ANNOTATIONS]?.toBooleanStrictOrNull() ?: true
this.types = processingEnv.typeUtils this.types = processingEnv.typeUtils
this.elements = processingEnv.elementUtils this.elements = processingEnv.elementUtils
@@ -135,11 +138,18 @@ public class JsonClassCodegenProcessor : AbstractProcessor() {
element: TypeElement, element: TypeElement,
cachedClassInspector: MoshiCachedClassInspector cachedClassInspector: MoshiCachedClassInspector
): AdapterGenerator? { ): AdapterGenerator? {
val type = targetType(messager, elements, types, element, cachedClassInspector) ?: return null val type = targetType(
messager,
elements,
types,
element,
cachedClassInspector,
instantiateAnnotations
) ?: 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) val generator = property.generator(messager, element, elements, type.instantiateAnnotations)
if (generator != null) { if (generator != null) {
properties[property.name] = generator properties[property.name] = generator
} }

View File

@@ -71,6 +71,7 @@ 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
@@ -122,7 +123,8 @@ internal fun targetType(
elements: Elements, elements: Elements,
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) {
@@ -204,6 +206,11 @@ 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)
@@ -319,7 +326,8 @@ internal fun targetType(
properties = properties, properties = properties,
typeVariables = typeVariables, typeVariables = typeVariables,
isDataClass = KModifier.DATA in kotlinApi.modifiers, isDataClass = KModifier.DATA in kotlinApi.modifiers,
visibility = resolvedVisibility visibility = resolvedVisibility,
instantiateAnnotations = instantiateAnnotations
) )
} }
@@ -418,7 +426,8 @@ private val TargetProperty.isVisible: Boolean
internal fun TargetProperty.generator( internal fun TargetProperty.generator(
messager: Messager, messager: Messager,
sourceElement: TypeElement, sourceElement: TypeElement,
elements: Elements elements: Elements,
instantiateAnnotations: Boolean
): PropertyGenerator? { ): PropertyGenerator? {
if (isTransient) { if (isTransient) {
if (!hasDefault) { if (!hasDefault) {
@@ -429,7 +438,7 @@ internal fun TargetProperty.generator(
) )
return null return null
} }
return PropertyGenerator(this, DelegateKey(type, emptyList()), true) return PropertyGenerator(this, DelegateKey(type, emptyList(), instantiateAnnotations), true)
} }
if (!isVisible) { if (!isVisible) {
@@ -452,6 +461,7 @@ internal fun TargetProperty.generator(
// Check Java types since that covers both Java and Kotlin annotations. // Check Java types since that covers both Java and Kotlin annotations.
val annotationElement = elements.getTypeElement(qualifierRawType.canonicalName) val annotationElement = elements.getTypeElement(qualifierRawType.canonicalName)
?: continue ?: continue
annotationElement.getAnnotation(Retention::class.java)?.let { annotationElement.getAnnotation(Retention::class.java)?.let {
if (it.value != RetentionPolicy.RUNTIME) { if (it.value != RetentionPolicy.RUNTIME) {
messager.printMessage( messager.printMessage(
@@ -460,12 +470,14 @@ internal fun TargetProperty.generator(
) )
} }
} }
annotationElement.getAnnotation(Target::class.java)?.let { if (!instantiateAnnotations) {
if (ElementType.FIELD !in it.value) { annotationElement.getAnnotation(Target::class.java)?.let {
messager.printMessage( if (ElementType.FIELD !in it.value) {
ERROR, messager.printMessage(
"JsonQualifier @${qualifierRawType.simpleName} must support FIELD target" ERROR,
) "JsonQualifier @${qualifierRawType.simpleName} must support FIELD target"
)
}
} }
} }
} }
@@ -478,7 +490,7 @@ internal fun TargetProperty.generator(
return PropertyGenerator( return PropertyGenerator(
this, this,
DelegateKey(type, jsonQualifierSpecs) DelegateKey(type, jsonQualifierSpecs, instantiateAnnotations)
) )
} }

View File

@@ -34,6 +34,7 @@ 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
@@ -63,6 +64,8 @@ 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 {
@@ -122,11 +125,11 @@ private class JsonClassSymbolProcessor(
resolver: Resolver, resolver: Resolver,
originalType: KSDeclaration, originalType: KSDeclaration,
): AdapterGenerator? { ): AdapterGenerator? {
val type = targetType(originalType, resolver, logger) ?: return null val type = targetType(originalType, resolver, logger, instantiateAnnotations) ?: 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) val generator = property.generator(logger, resolver, originalType, type.instantiateAnnotations)
if (generator != null) { if (generator != null) {
properties[property.name] = generator properties[property.name] = generator
} }

View File

@@ -69,12 +69,12 @@ private fun addValueToBlock(value: Any, resolver: Resolver, member: CodeBlock.Bu
when (value) { when (value) {
is List<*> -> { is List<*> -> {
// Array type // Array type
member.add("[⇥⇥") member.add("arrayOf(⇥⇥")
value.forEachIndexed { index, innerValue -> value.forEachIndexed { index, innerValue ->
if (index > 0) member.add(", ") if (index > 0) member.add(", ")
addValueToBlock(innerValue!!, resolver, member) addValueToBlock(innerValue!!, resolver, member)
} }
member.add("⇤⇤]") member.add("⇤⇤)")
} }
is KSType -> { is KSType -> {
val unwrapped = value.unwrapTypeAlias() val unwrapped = value.unwrapTypeAlias()

View File

@@ -28,17 +28,6 @@ import com.squareup.moshi.kotlin.codegen.api.PropertyGenerator
import com.squareup.moshi.kotlin.codegen.api.TargetProperty import com.squareup.moshi.kotlin.codegen.api.TargetProperty
import com.squareup.moshi.kotlin.codegen.api.rawType import com.squareup.moshi.kotlin.codegen.api.rawType
private val VISIBILITY_MODIFIERS = setOf(
KModifier.INTERNAL,
KModifier.PRIVATE,
KModifier.PROTECTED,
KModifier.PUBLIC
)
internal fun Collection<KModifier>.visibility(): KModifier {
return find { it in VISIBILITY_MODIFIERS } ?: KModifier.PUBLIC
}
private val TargetProperty.isTransient get() = propertySpec.annotations.any { it.typeName == Transient::class.asClassName() } private val TargetProperty.isTransient get() = propertySpec.annotations.any { it.typeName == Transient::class.asClassName() }
private val TargetProperty.isSettable get() = propertySpec.mutable || parameter != null private val TargetProperty.isSettable get() = propertySpec.mutable || parameter != null
private val TargetProperty.isVisible: Boolean private val TargetProperty.isVisible: Boolean
@@ -55,7 +44,8 @@ private val TargetProperty.isVisible: Boolean
internal fun TargetProperty.generator( internal fun TargetProperty.generator(
logger: KSPLogger, logger: KSPLogger,
resolver: Resolver, resolver: Resolver,
originalType: KSDeclaration originalType: KSDeclaration,
instantiateAnnotations: Boolean
): PropertyGenerator? { ): PropertyGenerator? {
if (isTransient) { if (isTransient) {
if (!hasDefault) { if (!hasDefault) {
@@ -65,7 +55,7 @@ internal fun TargetProperty.generator(
) )
return null return null
} }
return PropertyGenerator(this, DelegateKey(type, emptyList()), true) return PropertyGenerator(this, DelegateKey(type, emptyList(), instantiateAnnotations), true)
} }
if (!isVisible) { if (!isVisible) {
@@ -93,11 +83,13 @@ internal fun TargetProperty.generator(
) )
} }
} }
annotationElement.findAnnotationWithType<Target>()?.let { if (!instantiateAnnotations) {
if (AnnotationTarget.FIELD !in it.allowedTargets) { annotationElement.findAnnotationWithType<Target>()?.let {
logger.error( if (AnnotationTarget.FIELD !in it.allowedTargets) {
"JsonQualifier @${qualifierRawType.simpleName} must support FIELD target" logger.error(
) "JsonQualifier @${qualifierRawType.simpleName} must support FIELD target"
)
}
} }
} }
} }
@@ -111,7 +103,7 @@ internal fun TargetProperty.generator(
return PropertyGenerator( return PropertyGenerator(
this, this,
DelegateKey(type, jsonQualifierSpecs) DelegateKey(type, jsonQualifierSpecs, instantiateAnnotations)
) )
} }

View File

@@ -58,6 +58,7 @@ 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)
@@ -152,7 +153,8 @@ internal fun targetType(
properties = properties, properties = properties,
typeVariables = typeVariables, typeVariables = typeVariables,
isDataClass = Modifier.DATA in type.modifiers, isDataClass = Modifier.DATA in type.modifiers,
visibility = resolvedVisibility visibility = resolvedVisibility,
instantiateAnnotations = instantiateAnnotations
) )
} }

View File

@@ -18,9 +18,9 @@ package com.squareup.moshi.kotlin.codegen.apt
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonReader
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.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,6 +28,8 @@ 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
@@ -38,7 +40,24 @@ 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. */
class JsonClassCodegenProcessorTest { @RunWith(Parameterized::class)
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()
@Test @Test
@@ -455,8 +474,13 @@ class JsonClassCodegenProcessorTest {
""" """
) )
) )
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) if (languageVersion == "1.5" || !instantiateAnnotations) {
assertThat(result.messages).contains("JsonQualifier @UpperCase must support FIELD target") assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains("JsonQualifier @UpperCase must support FIELD target")
} else {
// We instantiate directly!
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
}
} }
@Test @Test
@@ -665,19 +689,34 @@ class JsonClassCodegenProcessorTest {
} }
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.UsingQualifiers" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.UsingQualifiers" -> {
""" if (languageVersion == "1.5" || !instantiateAnnotations) {
-if class testPackage.UsingQualifiers assertThat(generatedFile.readText()).contains(
-keepnames class testPackage.UsingQualifiers """
-if class testPackage.UsingQualifiers -if class testPackage.UsingQualifiers
-keep class testPackage.UsingQualifiersJsonAdapter { -keepnames class testPackage.UsingQualifiers
public <init>(com.squareup.moshi.Moshi); -if class testPackage.UsingQualifiers
private com.squareup.moshi.JsonAdapter stringAtMyQualifierAdapter; -keep class testPackage.UsingQualifiersJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
private com.squareup.moshi.JsonAdapter stringAtMyQualifierAdapter;
}
-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()
)
} }
-if class testPackage.UsingQualifiers }
-keep @interface testPackage.MyQualifier
""".trimIndent()
)
"moshi-testPackage.MixedTypes" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.MixedTypes" -> assertThat(generatedFile.readText()).contains(
""" """
-if class testPackage.MixedTypes -if class testPackage.MixedTypes
@@ -704,25 +743,46 @@ class JsonClassCodegenProcessorTest {
} }
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.Complex" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.Complex" -> {
""" if (languageVersion == "1.5" || !instantiateAnnotations) {
-if class testPackage.Complex assertThat(generatedFile.readText()).contains(
-keepnames class testPackage.Complex """
-if class testPackage.Complex -if class testPackage.Complex
-keep class testPackage.ComplexJsonAdapter { -keepnames class testPackage.Complex
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]); -if class testPackage.Complex
private com.squareup.moshi.JsonAdapter mutableListOfStringAtMyQualifierAdapter; -keep class testPackage.ComplexJsonAdapter {
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
private com.squareup.moshi.JsonAdapter mutableListOfStringAtMyQualifierAdapter;
}
-if class testPackage.Complex
-keep @interface testPackage.MyQualifier
-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()
)
} 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()
)
} }
-if class testPackage.Complex }
-keep @interface testPackage.MyQualifier
-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(
""" """
-if class testPackage.MultipleMasks -if class testPackage.MultipleMasks
@@ -739,19 +799,34 @@ class JsonClassCodegenProcessorTest {
} }
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.NestedType.NestedSimple" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.NestedType.NestedSimple" -> {
""" if (languageVersion == "1.5" || !instantiateAnnotations) {
-if class testPackage.NestedType${'$'}NestedSimple assertThat(generatedFile.readText()).contains(
-keepnames class testPackage.NestedType${'$'}NestedSimple """
-if class testPackage.NestedType${'$'}NestedSimple -if class testPackage.NestedType${'$'}NestedSimple
-keep class testPackage.NestedType_NestedSimpleJsonAdapter { -keepnames class testPackage.NestedType${'$'}NestedSimple
public <init>(com.squareup.moshi.Moshi); -if class testPackage.NestedType${'$'}NestedSimple
private com.squareup.moshi.JsonAdapter stringAtNestedQualifierAdapter; -keep class testPackage.NestedType_NestedSimpleJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
private com.squareup.moshi.JsonAdapter stringAtNestedQualifierAdapter;
}
-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()
)
} }
-if class testPackage.NestedType${'$'}NestedSimple }
-keep @interface testPackage.NestedType${'$'}NestedQualifier
""".trimIndent()
)
else -> error("Unexpected proguard file! ${generatedFile.name}") else -> error("Unexpected proguard file! ${generatedFile.name}")
} }
} }
@@ -765,6 +840,8 @@ 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,6 +16,7 @@
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
@@ -30,15 +31,41 @@ 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 kotlin.reflect.KClassifier import org.junit.runner.RunWith
import kotlin.reflect.KType import org.junit.runners.Parameterized
import kotlin.reflect.KTypeProjection
import kotlin.reflect.KVariance
import kotlin.reflect.KVariance.INVARIANT
import kotlin.reflect.full.createType
/** 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. */
class JsonClassSymbolProcessorTest { @RunWith(Parameterized::class)
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()
@@ -490,8 +517,13 @@ class JsonClassSymbolProcessorTest {
""" """
) )
) )
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR) if (languageVersion == "1.5" || !instantiateAnnotations) {
assertThat(result.messages).contains("JsonQualifier @UpperCase must support FIELD target") assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains("JsonQualifier @UpperCase must support FIELD target")
} else {
// We instantiate directly!
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
}
} }
@Test @Test
@@ -704,19 +736,34 @@ class JsonClassSymbolProcessorTest {
} }
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.UsingQualifiers" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.UsingQualifiers" -> {
""" if (languageVersion == "1.5" || !instantiateAnnotations) {
-if class testPackage.UsingQualifiers assertThat(generatedFile.readText()).contains(
-keepnames class testPackage.UsingQualifiers """
-if class testPackage.UsingQualifiers -if class testPackage.UsingQualifiers
-keep class testPackage.UsingQualifiersJsonAdapter { -keepnames class testPackage.UsingQualifiers
public <init>(com.squareup.moshi.Moshi); -if class testPackage.UsingQualifiers
private com.squareup.moshi.JsonAdapter stringAtMyQualifierAdapter; -keep class testPackage.UsingQualifiersJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
private com.squareup.moshi.JsonAdapter stringAtMyQualifierAdapter;
}
-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()
)
} }
-if class testPackage.UsingQualifiers }
-keep @interface testPackage.MyQualifier
""".trimIndent()
)
"moshi-testPackage.MixedTypes" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.MixedTypes" -> assertThat(generatedFile.readText()).contains(
""" """
-if class testPackage.MixedTypes -if class testPackage.MixedTypes
@@ -743,25 +790,46 @@ class JsonClassSymbolProcessorTest {
} }
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.Complex" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.Complex" -> {
""" if (languageVersion == "1.5" || !instantiateAnnotations) {
-if class testPackage.Complex assertThat(generatedFile.readText()).contains(
-keepnames class testPackage.Complex """
-if class testPackage.Complex -if class testPackage.Complex
-keep class testPackage.ComplexJsonAdapter { -keepnames class testPackage.Complex
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]); -if class testPackage.Complex
private com.squareup.moshi.JsonAdapter mutableListOfStringAtMyQualifierAdapter; -keep class testPackage.ComplexJsonAdapter {
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
private com.squareup.moshi.JsonAdapter mutableListOfStringAtMyQualifierAdapter;
}
-if class testPackage.Complex
-keep @interface testPackage.MyQualifier
-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()
)
} 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()
)
} }
-if class testPackage.Complex }
-keep @interface testPackage.MyQualifier
-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(
""" """
-if class testPackage.MultipleMasks -if class testPackage.MultipleMasks
@@ -778,19 +846,34 @@ class JsonClassSymbolProcessorTest {
} }
""".trimIndent() """.trimIndent()
) )
"moshi-testPackage.NestedType.NestedSimple" -> assertThat(generatedFile.readText()).contains( "moshi-testPackage.NestedType.NestedSimple" -> {
""" if (languageVersion == "1.5" || !instantiateAnnotations) {
-if class testPackage.NestedType${'$'}NestedSimple assertThat(generatedFile.readText()).contains(
-keepnames class testPackage.NestedType${'$'}NestedSimple """
-if class testPackage.NestedType${'$'}NestedSimple -if class testPackage.NestedType${'$'}NestedSimple
-keep class testPackage.NestedType_NestedSimpleJsonAdapter { -keepnames class testPackage.NestedType${'$'}NestedSimple
public <init>(com.squareup.moshi.Moshi); -if class testPackage.NestedType${'$'}NestedSimple
private com.squareup.moshi.JsonAdapter stringAtNestedQualifierAdapter; -keep class testPackage.NestedType_NestedSimpleJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
private com.squareup.moshi.JsonAdapter stringAtNestedQualifierAdapter;
}
-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()
)
} }
-if class testPackage.NestedType${'$'}NestedSimple }
-keep @interface testPackage.NestedType${'$'}NestedQualifier
""".trimIndent()
)
else -> error("Unexpected proguard file! ${generatedFile.name}") else -> error("Unexpected proguard file! ${generatedFile.name}")
} }
} }
@@ -805,20 +888,12 @@ 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"
} }
} }
private fun compile(vararg sourceFiles: SourceFile): KotlinCompilation.Result { private fun compile(vararg sourceFiles: SourceFile): KotlinCompilation.Result {
return prepareCompilation(*sourceFiles).compile() return prepareCompilation(*sourceFiles).compile()
} }
private fun KClassifier.parameterizedBy(vararg types: KType): KType {
return createType(
types.map { it.asProjection() }
)
}
private fun KType.asProjection(variance: KVariance? = INVARIANT): KTypeProjection {
return KTypeProjection(variance, this)
}
} }

View File

@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
kotlin("jvm") kotlin("jvm")
kotlin("kapt") apply false kotlin("kapt") apply false
alias(libs.plugins.ksp) apply false id("com.google.devtools.ksp") apply false
} }
val useKsp = hasProperty("useKsp") val useKsp = hasProperty("useKsp")
@@ -34,11 +34,14 @@ tasks.withType<Test>().configureEach {
jvmArgs("--add-opens=java.base/java.io=ALL-UNNAMED") jvmArgs("--add-opens=java.base/java.io=ALL-UNNAMED")
} }
val useWError = findProperty("kotlinLanguageVersion")?.toString()
?.startsWith("1.5")
?: false
tasks.withType<KotlinCompile>().configureEach { tasks.withType<KotlinCompile>().configureEach {
kotlinOptions { kotlinOptions {
allWarningsAsErrors = useWError
@Suppress("SuspiciousCollectionReassignment") @Suppress("SuspiciousCollectionReassignment")
freeCompilerArgs += listOf( freeCompilerArgs += listOf(
"-Werror",
"-Xopt-in=kotlin.ExperimentalStdlibApi" "-Xopt-in=kotlin.ExperimentalStdlibApi"
) )
} }