autonomousapps/dependency-analysis-gradle-plugin

Incorrect advice to change dependency from implementation to api

Ouverte

#1 118 ouverte le 31 janv. 2024

 (7 commentaires) (0 réaction) (0 personne assignée)Kotlin (154 forks)auto 404
bughelp wantedtoolchain:androidtoolchain:kotlin

Métriques du dépôt

Stars
 (2 172 étoiles)
Métriques de merge PR
 (Métriques PR en attente)

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:

  1. Create library module, for example uikit with 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

Guide contributeur