evant / kotlin-inject

Dependency injection lib for kotlin
Apache License 2.0
1.14k stars 51 forks source link

Build fails with multi module android project with Kotlin 2.0.0 #388

Open julioromano opened 1 month ago

julioromano commented 1 month ago

Repro project: https://github.com/julioromano/kotlin-inject-bug-repro-1 It's got 2 commit, the 1st commit builds fine, the second triggers the error.

e: [ksp] Cannot find an @Inject constructor or provider for: Function1<kotlin.Function1<kotlin.Long, kotlin.Unit>, kotlin.Unit>
com.example.foo.TrendingNavigation(trendingScreen: Function1<kotlin.Function1<kotlin.Long, kotlin.Unit>, kotlin.Unit>)

The difference between the commits is that some code has been moved out from the app module to an external one.

Slack convo: https://kotlinlang.slack.com/archives/C0255B8KX7W/p1716363571041319

eygraber commented 1 month ago

I tried that project with 0.7.0 and https://github.com/evant/kotlin-inject/pull/349 and it still doesn't work.

The issue seems to be that KSP in app is looking for the signature of TrendingScreen before it has been transformed by the Compose compiler, i.e. @Composable ((id: Long) -> Unit) -> Unit is seen as

Function1<Function1<Long, Unit>, Unit>

However, looking at TrendingNavigation.class, the Compose compiler has transformed it into:

Function3<Function1<Long, Unit>, Composer, Integer, Unit>

I'm not sure if this is an issue in kotlin-inject, KSP, or Compose.

eygraber commented 1 month ago

Hmm maybe that's not it, because the first commit where it works sees the type the same way.

The first commit fails on 0.7.0-SNAPSHOT though, because it needs to use @Assisted. I don't know much about how function injection works, but I think there were some changes made there so maybe it is related to that.

eygraber commented 1 month ago

Did some more digging, and it looks like in the working case, the TrendingScreen typealias is maintained, but in the broken case it is not a typealias, but is already resolved.

eygraber commented 1 month ago

And looks like it's this issue in KSP - https://github.com/google/ksp/issues/1849

julioromano commented 1 month ago

Interestingly enough I get a very similar failure when setting ksp.useKSP2=true even with the 1st commit.

e: [ksp] Cannot find an @Inject constructor or provider for: Function1<kotlin.Long, kotlin.Unit>
/Users/julionb/Develop/marco/kotlin-inject-bug-repro-1/app/src/main/java/com/example/myapplication/TrendingNavigation.kt:18: TrendingScreen(trendingViewModel: Function0<com.example.myapplication.TrendingViewModel>, navToDetail: Function1<kotlin.Long, kotlin.Unit>): Unit
/Users/julionb/Develop/marco/kotlin-inject-bug-repro-1/app/src/main/java/com/example/myapplication/TrendingNavigation.kt:10: com.example.myapplication.TrendingNavigation(trendingScreen: com.example.myapplication.TrendingScreen)
/Users/julionb/Develop/marco/kotlin-inject-bug-repro-1/app/src/main/java/com/example/myapplication/ApplicationComponent.kt:11: trendingNavigation: com.example.myapplication.TrendingNavigation
eygraber commented 1 month ago

OK probably not related to that issue. I filed a new one - https://github.com/google/ksp/issues/1921

julioromano commented 1 month ago

FYI: As a workaround I had to inject composables through wrapper classes instead of using type aliases.

typealias TrendingScreen = @Composable ((id: Long) -> Unit) -> Unit

@Inject
@Composable
fun TrendingScreen(
    trendingViewModel: () -> TrendingViewModel,
    navToDetail: (id: Long) -> Unit,
) {
    val vm = remember { trendingViewModel() }
    Text(text = "I'm just a placeholder")
}

becomes

public class TrendingScreen @Inject constructor(
  private val trendingViewModel: () -> TrendingViewModel,
) {
  @Composable
  public fun Composable(navToDetail: (id: Long) -> Unit) {
    val vm = viewModel { trendingViewModel() }
    Text(text = "I'm just a placeholder")
  }
}