Skip to content

Commit

Permalink
Use KSP2 (#350)
Browse files Browse the repository at this point in the history
* Enable KSP2

* Remove val from function parameter in test

* Use messagesWithSeverity for tests

* Kotlin 2.0.21

* Update yarn.lock

* Don't cache elements across rounds

* Fix test failures due to logging

* Fix typealiases for function injection

* Fix typealiases with type parameters

* KSP2 returns synthetic constructors for objects

* Defer all KmpComponentCreate functions to the last round

* Handle typealias to typealias

* Added a KSP1 target for unit tests

* Add KSP1 integration tests

* Clean in between assemble and check on CI

  - Avoids issues until google/ksp#2231 is released

* Update Kotlin to 2.1.0

* Update KSP to 2.1.0-1.0.29

* Update kotlin-compile-testing

* Rename resolveToHighestTypeAlias for clarity

* Remove unused property
  • Loading branch information
eygraber authored Jan 10, 2025
1 parent 7f7dcd9 commit c385b80
Show file tree
Hide file tree
Showing 26 changed files with 506 additions and 267 deletions.
6 changes: 5 additions & 1 deletion ast/ksp/src/main/kotlin/me/tatarka/kotlin/ast/KSAst.kt
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,11 @@ private class KSAstType private constructor(
override fun resolvedType(): AstType {
val declaration = type.declaration
return if (declaration is KSTypeAlias) {
KSAstType(resolver, declaration.type.resolve().replace(type.arguments))
if (type.arguments.isEmpty()) {
KSAstType(resolver, declaration.type.resolve())
} else {
KSAstType(resolver, declaration.type.resolve().replace(type.arguments))
}
} else {
this
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.kotlin.dsl.assign
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ org.gradle.parallel=true
kotlin.mpp.stability.nowarn=true
kotlin.native.ignoreDisabledTargets=true
org.gradle.jvmargs=-Xmx4G
ksp.useKSP2=false
ksp.useKSP2=true
7 changes: 4 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[versions]
kotlin-inject = "0.7.3-SNAPSHOT"
kotlin = "2.0.10"
ksp = "2.0.10-1.0.24"
kotlin = "2.1.0"
ksp = "2.1.0-1.0.29"
kotlinpoet = "2.0.0"
junit5 = "5.9.3"
jvmTarget = "11"
Expand All @@ -13,10 +13,11 @@ detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting",
ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-compile-testing = { module = "dev.zacsweers.kctfork:ksp", version = "0.4.1" }
kotlin-compile-testing = { module = "dev.zacsweers.kctfork:ksp", version = "0.7.0" }
kotlin-metadata-jvm = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version.ref = "kotlin" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.8.0" }
kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version = "0.23.2" }
kotlinx-wasm-browser = "org.jetbrains.kotlinx:kotlinx-browser:0.3"
jdk-compiler = { module = "io.earcam.wrapped:jdk.compiler", version = "1.8.132" }
assertk = { module = "com.willowtreeapps.assertk:assertk", version = "0.28.0" }
burst-junit4 = { module = "com.squareup.burst:burst-junit4", version = "1.2.0" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package me.tatarka.inject.test

import assertk.assertThat
import assertk.assertions.isNotNull
import kotlin.test.Test

class TypeAliasTest {

@Test
fun can_generate_for_typealias_on_typealias_function() {
val component = TypeAliasesComponent::class.create()

assertThat(component.typeAliasFoo()).isNotNull()
}

@Test
fun can_generate_for_typealias_on_typealias_to_typealias_function() {
val component = TypeAliasesComponent::class.create()

assertThat(component.typeAliasToTypeAliasFoo()).isNotNull()
}

@Test
fun can_generate_for_typealias_on_typealias_to_typealias_to_typealias_function() {
val component = TypeAliasesComponent::class.create()

assertThat(component.typeAliasToTypeAliasToTypeAliasFoo()).isNotNull()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package me.tatarka.inject.test

import me.tatarka.inject.annotations.Component
import me.tatarka.inject.annotations.Inject

@Inject
class TypeAliasBar

typealias TypeAliasToTypeAliasBar = TypeAliasBar
typealias TypeAliasToTypeAliasToTypeAliasBar = TypeAliasToTypeAliasBar

@Inject
fun TypeAliasFoo(
bar: TypeAliasBar,
typeAliasToBar: TypeAliasToTypeAliasBar,
typeAliasToTypeAliasToBar: TypeAliasToTypeAliasToTypeAliasBar,
) {}
typealias TypeAliasFoo = () -> Unit

typealias TypeAliasToTypeAliasFoo = TypeAliasFoo
typealias TypeAliasToTypeAliasToTypeAliasFoo = TypeAliasToTypeAliasFoo

@Component
abstract class TypeAliasesComponent {
abstract fun typeAliasFoo(): TypeAliasFoo
abstract fun typeAliasToTypeAliasFoo(): TypeAliasToTypeAliasFoo
abstract fun typeAliasToTypeAliasToTypeAliasFoo(): TypeAliasToTypeAliasToTypeAliasFoo
}
5 changes: 5 additions & 0 deletions integration-tests/ksp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ kotlin {
implementation(libs.kotlin.reflect)
}
}
wasmJsMain {
dependencies {
implementation(libs.kotlinx.wasm.browser)
}
}
}

targets.all {
Expand Down
1 change: 1 addition & 0 deletions integration-tests/ksp1-companion/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
48 changes: 48 additions & 0 deletions integration-tests/ksp1-companion/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import com.google.devtools.ksp.KspExperimental

plugins {
id("kotlin-inject.multiplatform")
id("kotlin-inject.detekt")
id("kotlin-inject.merge-tests")
id("com.google.devtools.ksp")
}

dependencies {
addAllKspTargets(kotlin, project(":kotlin-inject-compiler:kotlin-inject-compiler-ksp"))
}

kotlin {
jvm {
withJava()
}

sourceSets {
commonMain {
kotlin.srcDir("../common-companion/src/main/kotlin")
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
dependencies {
implementation(project(":kotlin-inject-runtime-kmp"))
implementation(project(":integration-tests:module"))
}
}
commonTest {
kotlin.srcDir("../common-companion-test/src/test/kotlin")
dependencies {
implementation(libs.kotlin.test)
implementation(libs.assertk)
}
}
nativeMain {
kotlin.srcDir("../common-native/src/main/kotlin")
}
nativeTest {
kotlin.srcDir("../common-native/src/test/kotlin")
}
}
}

ksp {
arg("me.tatarka.inject.generateCompanionExtensions", "true")
@OptIn(KspExperimental::class)
useKsp2 = false
}
1 change: 1 addition & 0 deletions integration-tests/ksp1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
81 changes: 81 additions & 0 deletions integration-tests/ksp1/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import com.google.devtools.ksp.KspExperimental

plugins {
id("kotlin-inject.multiplatform")
id("kotlin-inject.detekt")
id("kotlin-inject.merge-tests")
id("com.google.devtools.ksp")
}

dependencies {
addAllKspTargets(kotlin, project(":kotlin-inject-compiler:kotlin-inject-compiler-ksp"))
kspJvmTest(project(":kotlin-inject-compiler:kotlin-inject-compiler-ksp"))
}

kotlin {
jvm {
withJava()
}

sourceSets {
commonMain {
kotlin.srcDir("../common/src/main/kotlin")
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
dependencies {
implementation(project(":kotlin-inject-runtime-kmp"))
implementation(project(":integration-tests:module"))
}
}
commonTest {
kotlin.srcDir("../common-test/src/test/kotlin")
dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines)
implementation(libs.assertk)
}
}
nativeMain {
kotlin.srcDir("../common-native/src/main/kotlin")
}
nativeTest {
kotlin.srcDir("../common-native/src/test/kotlin")
}
jvmMain {
kotlin.srcDir("../common-jvm/src/main/kotlin")
dependencies {
api(libs.javax.inject)
}
}
jvmTest {
kotlin.srcDir("../common-jvm/src/test/kotlin")
dependencies {
implementation(libs.kotlin.reflect)
}
}
wasmJsMain {
dependencies {
implementation(libs.kotlinx.wasm.browser)
}
}
}

targets.all {
compilations.all {
compileTaskProvider.configure {
compilerOptions.allWarningsAsErrors = true
}
}
}
}

java {
val test by sourceSets.existing {
java.srcDir("../common-jvm/src/test/java")
}
}

ksp {
arg("me.tatarka.inject.enableJavaxAnnotations", "true")
@OptIn(KspExperimental::class)
useKsp2 = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package me.tatarka.inject.test

import kotlinx.coroutines.CoroutineScope

/**
* Workaround to use suspending functions in unit tests
*/
expect fun runTest(block: suspend (scope: CoroutineScope) -> Unit)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package me.tatarka.inject.test

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.promise

/**
* Workaround to use suspending functions in unit tests
*/
@OptIn(DelicateCoroutinesApi::class)
actual fun runTest(block: suspend (scope: CoroutineScope) -> Unit) {
GlobalScope.promise { block(this) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package me.tatarka.inject.test

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking

/**
* Workaround to use suspending functions in unit tests
*/
actual fun runTest(block: suspend (scope: CoroutineScope) -> Unit) = runBlocking { block(this) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package me.tatarka.inject.test

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking

/**
* Workaround to use suspending functions in unit tests
*/
actual fun runTest(block: suspend (scope: CoroutineScope) -> Unit) = runBlocking { block(this) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package me.tatarka.inject.test

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.promise

/**
* Workaround to use suspending functions in unit tests
*/
@OptIn(DelicateCoroutinesApi::class)
actual fun runTest(block: suspend (scope: CoroutineScope) -> Unit) {
GlobalScope.promise { block(this) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,9 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
var assistedFailed = false
val args = context.args.toMutableList()
for (param in params) {
val type = if (param.type.isPlatform()) {
param.type.makeNonNullable()
} else {
param.type
}
val resolvedType = if (param.type.isTypeAlias()) param.type.resolvedType() else param.type
val type = if (resolvedType.isPlatform()) resolvedType.makeNonNullable() else resolvedType

val qualifier = qualifier(provider, options, param, type)
val key = TypeKey(type, qualifier)
if (param.isAssisted()) {
Expand Down Expand Up @@ -232,7 +230,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
return map(key)
}

if (key.type.isFunction()) {
if (key.type.isFunctionOrTypeAliasOfFunction()) {
return functionType(element, key)
}

Expand All @@ -244,15 +242,16 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
}

val astClass = key.type.toAstClass()

if (astClass.isObject && astClass.isInject()) {
return Object(astClass.type)
}

val injectCtor = astClass.findInjectConstructors(provider.messenger, options)
if (injectCtor != null) {
return constructor(key, injectCtor, astClass)
}

if (astClass.isInject() && astClass.isObject) {
return Object(astClass.type)
}

if (astClass.isAssistedFactory()) {
return assistedFactory(astClass, key)
}
Expand Down Expand Up @@ -383,8 +382,10 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
val resolveType = key.type.resolvedType()
val args = resolveType.arguments.dropLast(1)
if (key.type.isTypeAlias()) {
val resolvedTypeAlias = key.type.fullyResolvedType()

// Check to see if we have a function matching the type alias
val functions = provider.findFunctions(key.type.packageName, key.type.simpleName)
val functions = provider.findFunctions(resolvedTypeAlias.packageName, resolvedTypeAlias.simpleName)
val injectedFunction = functions.find { it.isInject() }
if (injectedFunction != null) {
return NamedFunction(
Expand Down Expand Up @@ -654,4 +655,4 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
// default values are present.
val args: List<Pair<AstType, String>>,
)
}
}
Loading

0 comments on commit c385b80

Please sign in to comment.