Plugin version
3.17.0
Gradle version
9.6.1
JDK version
26
Kotlin and Kotlin Gradle Plugin (KGP) version
2.4.10
Android Gradle Plugin (AGP) version
9.3.0
reason output for bugs relating to incorrect advice
------------------------------------------------------------
You asked about the dependency 'com.michael-bull.kotlin-result:kotlin-result-coroutines:2.3.1 (libs.kotlin.result.coroutines)'.
You have been advised to remove this dependency from 'commonMainImplementation'.
------------------------------------------------------------
Shortest path from :core:database to com.michael-bull.kotlin-result:kotlin-result-coroutines:2.3.1 (libs.kotlin.result.coroutines) for androidCompileClasspath:
:core:database
\--- com.michael-bull.kotlin-result:kotlin-result-coroutines:2.3.1
Source: androidMain
-------------------
(no usages)
Following the advice fails compilation:
e: file://.../core/database/src/commonMain/kotlin/.../DefaultLocalDataSource.kt:9:38 Unresolved reference 'coroutines'.
e: file://.../core/database/src/commonMain/kotlin/.../DefaultLocalDataSource.kt:51:16 Unresolved reference 'runSuspendCatching'.
Describe the bug
In a KMP module, a commonMain dependency whose only used API is an inline function is reported as unused, even though removing it breaks compilation.
The module has in commonMain:
import com.github.michaelbull.result.coroutines.runSuspendCatching
runSuspendCatching is inline, which it seems like DAGP handles with import matching (FindKotlinMagicTask, ComputeUsagesTask.usesInlineMember), which compares the project's source imports against inline members found in dependency jars.
I did some digging and the producer half works. intermediates/inline-usage.json for the androidMain analysis correctly lists the artifact's inline members:
{
"coordinates": { "identifier": "com.michael-bull.kotlin-result:kotlin-result-coroutines-jvm", ... },
"inlineMembers": [
{ "className": "com.github.michaelbull.result.coroutines.RunSuspendCatchingKt",
"packageName": "com.github.michaelbull.result.coroutines",
"inlineMembers": ["runSuspendCatching"] }, ...
]
}
The consumer half never sees the import. intermediates/exploded-source.json for the same analysis contains only the androidMain file:
[ { "relativePath": "src/androidMain/kotlin/.../DatabaseTypeFactory.android.kt", ... } ]
The commonMain file containing the runSuspendCatching import is absent, so usesInlineMember finds no matching import and the dependency falls through to Bucket.NONE.
The cause appears to be in source collection for KMP compilations. KmpSourceSet uses only the compilation's default source set:
// internal/analyzer/JvmSourceSet.kt
override val sourceCode: SourceDirectorySet = compilation.defaultSourceSet.kotlin
For the Android compilation of a KMP module, defaultSourceSet is androidMain; the dependsOn closure (commonMain, nativeMain, etc.) is not included. So any usage evidence that exists only in common source (e.g. inline function imports) is invisible to every per-compilation analysis. (KotlinCommonSources.kt collects commonMain/commonTest sources, but its only caller is the unused ListSourceFilesTask.)
To Reproduce
- Create a KMP library module with an Android target.
- In
commonMain, declare implementation("com.michael-bull.kotlin-result:kotlin-result-coroutines:2.3.1").
- In
commonMain source, call runSuspendCatching { ... } (its result must flow into non-inline API from the base kotlin-result artifact, which is typical).
- Run
buildHealth: the dependency is advised for removal from commonMainImplementation.
- Remove it: compilation fails with
Unresolved reference 'runSuspendCatching'.
Expected behavior
The usage of the inline function is picked up.
Additional context
Workaround:
dependencyAnalysis {
issues {
all {
onUnusedDependencies {
exclude("com.michael-bull.kotlin-result:kotlin-result-coroutines")
}
}
}
}
Plugin version
3.17.0
Gradle version
9.6.1
JDK version
26
Kotlin and Kotlin Gradle Plugin (KGP) version
2.4.10
Android Gradle Plugin (AGP) version
9.3.0
reasonoutput for bugs relating to incorrect adviceFollowing the advice fails compilation:
Describe the bug
In a KMP module, a
commonMaindependency whose only used API is aninlinefunction is reported as unused, even though removing it breaks compilation.The module has in
commonMain:runSuspendCatchingisinline, which it seems like DAGP handles with import matching (FindKotlinMagicTask,ComputeUsagesTask.usesInlineMember), which compares the project's source imports against inline members found in dependency jars.I did some digging and the producer half works.
intermediates/inline-usage.jsonfor theandroidMainanalysis correctly lists the artifact's inline members:{ "coordinates": { "identifier": "com.michael-bull.kotlin-result:kotlin-result-coroutines-jvm", ... }, "inlineMembers": [ { "className": "com.github.michaelbull.result.coroutines.RunSuspendCatchingKt", "packageName": "com.github.michaelbull.result.coroutines", "inlineMembers": ["runSuspendCatching"] }, ... ] }The consumer half never sees the import.
intermediates/exploded-source.jsonfor the same analysis contains only theandroidMainfile:[ { "relativePath": "src/androidMain/kotlin/.../DatabaseTypeFactory.android.kt", ... } ]The
commonMainfile containing therunSuspendCatchingimport is absent, sousesInlineMemberfinds no matching import and the dependency falls through toBucket.NONE.The cause appears to be in source collection for KMP compilations.
KmpSourceSetuses only the compilation's default source set:For the Android compilation of a KMP module,
defaultSourceSetisandroidMain; thedependsOnclosure (commonMain,nativeMain, etc.) is not included. So any usage evidence that exists only in common source (e.g. inline function imports) is invisible to every per-compilation analysis. (KotlinCommonSources.ktcollectscommonMain/commonTestsources, but its only caller is the unusedListSourceFilesTask.)To Reproduce
commonMain, declareimplementation("com.michael-bull.kotlin-result:kotlin-result-coroutines:2.3.1").commonMainsource, callrunSuspendCatching { ... }(its result must flow into non-inline API from the basekotlin-resultartifact, which is typical).buildHealth: the dependency is advised for removal fromcommonMainImplementation.Unresolved reference 'runSuspendCatching'.Expected behavior
The usage of the inline function is picked up.
Additional context
Workaround:
dependencyAnalysis { issues { all { onUnusedDependencies { exclude("com.michael-bull.kotlin-result:kotlin-result-coroutines") } } } }