Incorrect advice to change dependency from implementation to api
#1,118 opened on Jan 31, 2024
Repository metrics
- Stars
- (2,172 stars)
- PR merge metrics
- (PR metrics pending)
Description
Plugin version "1.29.0"
Gradle version Gradle-8.2
JDK version openjdk version "17" 2021-09-14 OpenJDK Runtime Environment (build 17+35-2724) OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)
(Optional) Kotlin and Kotlin Gradle Plugin (KGP) version "1.9.0"
(Optional) Android Gradle Plugin (AGP) version 8.2.0
(Optional) reason output for bugs relating to incorrect advice
> Task :feature:reason
----------------------------------------
You asked about the dependency 'androidx.compose.foundation:foundation-layout-android:1.6.0'.
You have been advised to change this dependency to 'api' from 'implementation'.
----------------------------------------
Shortest path from :feature to androidx.compose.foundation:foundation-layout-android:1.6.0 for debugCompileClasspath:
:feature
\--- androidx.compose.foundation:foundation-layout-android:1.6.0
<TRUNC>
Source: debug, main
-------------------
* Exposes 1 class: androidx.compose.foundation.layout.BoxScope (implies api).
Describe the bug As per this article: https://dev.to/autonomousapps/dependency-analysis-gradle-plugin-what-s-an-abi-3l2h this plugin uses application binary interface as a source of advices. Unfortunately, compiler can add some optimizations, such as cache for lambdas:
@Lkotlin/Metadata;
public final class com/github/dependency/analysis/feature/ComposableSingletons$FeatureElementKt {
public static final field INSTANCE Lcom/github/dependency/analysis/feature/ComposableSingletons$FeatureElementKt;
public static field lambda-1 Lkotlin/jvm/functions/Function3;
public fun <init> ()V
public final fun getLambda-1$feature_debug ()Lkotlin/jvm/functions/Function3;
}
And due to these transformations it is possible, that some classes will be treated as exposed (BoxScope from steps to reproduce section), but in fact they are not a part of actual public module API.
To Reproduce Steps to reproduce the behavior:
- Create library module, for example
uikitwith compose elements for android development.
@Composable
fun BaseFooter(subContent: @Composable BoxScope.() -> Unit) {
Box(modifier = Modifier.fillMaxSize()) {
subContent()
}
Text(text = "footer")
}
It exposes BoxScope, hence has api("androidx.compose.foundation:foundation-layout-android")
2) Create feature module and use uikit as a dependency:
@Composable
fun FeatureElement(text: String) {
Text(text = text)
// It triggers incorrect api suggestion due to cached lambda
BaseFooter {}
// This variant pass without any advice
// val someString = remember { "text" }
// BaseFooter { println(someString) }
}
This module doesn't expose BoxScope directly, just uses it internally, but plugin treats it as public usage.
Expected behavior
Plugin shouldn't advice to change from implementation("androidx.compose.foundation:foundation-layout-android") to api("androidx.compose.foundation:foundation-layout-android") for feature module.
Additional context
Test project: DependencyAnalysisTest.zip (don't forget to add local.properties with sdk.dir=YOUR_PATH)
Launch the projectHealth task for feature module
./gradlew :feature:projectHealth --no-configuration-cache
and check reason:
./gradlew :feature:reason --no-configuration-cache --id androidx.compose.foundation:foundation-layout-android