Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ syntax:
+ jsonWriter.indent = " "
```

### moshi-kotlin-codegen

The KSP code generator now supports (and requires) KSP2.

## Version 1.15.2

Expand Down
4 changes: 1 addition & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import com.diffplug.gradle.spotless.JavaExtension
import com.google.devtools.ksp.gradle.KspTaskJvm
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import org.jetbrains.dokka.gradle.DokkaExtension
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
Expand Down Expand Up @@ -90,9 +89,8 @@ subprojects {

pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
tasks.withType<KotlinCompile>().configureEach {
val isKsp1Task = this is KspTaskJvm
compilerOptions {
progressiveMode.set(!isKsp1Task)
progressiveMode.set(true)
jvmTarget.set(JvmTarget.fromTarget(libs.versions.jvmTarget.get()))
}
}
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ autoService = "1.1.1"
jdk = "21"
jvmTarget = "1.8"
kotlin = "2.2.21"
kotlinCompileTesting = "0.9.0"
kotlinCompileTesting = "0.11.1"
kotlinpoet = "2.2.0"
ksp = "2.2.20-2.0.4"
ksp = "2.3.3"

[plugins]
dokka = { id = "org.jetbrains.dokka", version = "2.1.0" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,12 @@ import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSName
import com.google.devtools.ksp.symbol.KSNode
import com.google.devtools.ksp.symbol.KSType
import com.google.devtools.ksp.symbol.KSTypeAlias
import com.google.devtools.ksp.symbol.Origin.KOTLIN
import com.google.devtools.ksp.symbol.Origin.KOTLIN_LIB
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.ksp.toClassName

Expand Down Expand Up @@ -59,20 +57,20 @@ internal fun KSAnnotation.toAnnotationSpec(resolver: Resolver): AnnotationSpec {
val member = CodeBlock.builder()
val name = argument.name!!.getShortName()
member.add("%L = ", name)
addValueToBlock(argument.value!!, resolver, member)
addValueToBlock(argument.value!!, resolver, member, element)
builder.addMember(member.build())
}
return builder.build()
}

private fun addValueToBlock(value: Any, resolver: Resolver, member: CodeBlock.Builder) {
private fun addValueToBlock(value: Any, resolver: Resolver, member: CodeBlock.Builder, annotationContext: KSClassDeclaration? = null) {
when (value) {
is List<*> -> {
// Array type
member.add("arrayOf(⇥⇥")
value.forEachIndexed { index, innerValue ->
if (index > 0) member.add(", ")
addValueToBlock(innerValue!!, resolver, member)
addValueToBlock(innerValue!!, resolver, member, annotationContext)
}
member.add("⇤⇤)")
}
Expand All @@ -89,12 +87,21 @@ private fun addValueToBlock(value: Any, resolver: Resolver, member: CodeBlock.Bu
}
}

is KSName ->
member.add(
"%T.%L",
ClassName.bestGuess(value.getQualifier()),
value.getShortName(),
)
is KSClassDeclaration -> {
// Handle enum entries that come directly as KSClassDeclaration
if (value.classKind == ClassKind.ENUM_ENTRY) {
val enumEntry = value.simpleName.getShortName()
val parentClass = value.parentDeclaration as? KSClassDeclaration

if (parentClass != null && parentClass.classKind == ClassKind.ENUM_CLASS) {
member.add("%T.%L", parentClass.toClassName(), enumEntry)
} else {
member.add("%L", enumEntry)
}
} else {
member.add("%T::class", value.toClassName())
}
}

is KSAnnotation -> member.add("%L", value.toAnnotationSpec(resolver))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,13 @@ import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import kotlin.reflect.KTypeProjection
import kotlin.reflect.full.createType
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.typeOf

/** Execute kotlinc to confirm that either files are generated or errors are printed. */
@RunWith(Parameterized::class)
class JsonClassSymbolProcessorTest(private val useKSP2: Boolean) {

companion object {
@JvmStatic
@Parameterized.Parameters(name = "useKSP2={0}")
fun data(): Collection<Array<Any>> = listOf(
arrayOf(false),
arrayOf(true),
)
}
class JsonClassSymbolProcessorTest {

@Rule
@JvmField
Expand Down Expand Up @@ -848,13 +836,8 @@ class JsonClassSymbolProcessorTest(private val useKSP2: Boolean) {
inheritClassPath = true
sources = sourceFiles.asList()
verbose = false
configureKsp(useKsp2 = useKSP2) {
configureKsp {
symbolProcessorProviders += JsonClassSymbolProcessorProvider()
incremental = true // The default now
if (!useKSP2) {
withCompilation = true // Only necessary for KSP1
languageVersion = "1.9"
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1196,7 +1196,12 @@ class GeneratedAdaptersTest {
}

@JsonQualifier
annotation class Uppercase(val inFrench: Boolean, val onSundays: Boolean = false)
annotation class Uppercase(val inFrench: Boolean, val onSundays: Boolean = false, val temperature: Temperature = Temperature.COLD) {
enum class Temperature {
WARM,
COLD,
}
}

class UppercaseJsonAdapter {
@ToJson
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.squareup.moshi.FromJson
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import com.squareup.moshi.JsonDataException
import com.squareup.moshi.JsonQualifier
import com.squareup.moshi.Moshi
import com.squareup.moshi.ToJson
import com.squareup.moshi.Types
Expand Down Expand Up @@ -762,6 +763,41 @@ class DualKotlinTest {
val `$a`: String,
@Json(name = "\$b") val b: String,
)

@Retention(RUNTIME)
@JsonQualifier
annotation class NestedEnum(val nested: Nested = Nested.A) {
enum class Nested { A, B, C }
}

@Test fun nestedEnumAnnotation() {
val moshi = Moshi.Builder()
.add(
object {
@FromJson
@NestedEnum
fun fromJson(string: String): String? = string

@ToJson
fun toJson(@NestedEnum @Nullable value: String?): String {
return value ?: "fallback"
}
},
)
.add(KotlinJsonAdapterFactory()).build()
val jsonAdapter = moshi.adapter<PropertyWithNestedEnumAnnotation>()

val value = PropertyWithNestedEnumAnnotation("apple")
val json = """{"value":"apple"}"""
assertThat(jsonAdapter.toJson(value)).isEqualTo(json)
assertThat(jsonAdapter.fromJson(json)).isEqualTo(value)
}

@JsonClass(generateAdapter = true)
data class PropertyWithNestedEnumAnnotation(
@NestedEnum
val value: String,
)
}

typealias TypeAlias = Int
Expand Down