Upgrade to Pro — share decks privately, control downloads, hide ads and more …

KotlinConf 24 - Dynamic Exploration of Static A...

KotlinConf 24 - Dynamic Exploration of Static Analysis with Compose

Video: https://www.youtube.com/watch?v=vRzE6HIz-_M
KotlinConf Talk: https://kotlinconf.com/talks/586687/

Static analysis is so much more than just finding lint errors. With custom static analyzers, you can collect data you are interested in to better understand your codebase at scale.

I personally found the idea of collecting custom analysis to be unapproachable and I didn’t know where to start. After making it through, and finding so much value, I want to break down that barrier for you through a series of examples. These examples are based on real use cases where you will learn how to write your own static analysis collectors for Kotlin, how to visualize the analysis results with Compose, and find hidden insights into your team’s project.

We’ll start by learning how to create a custom static analyzer of your project via Kotlin's Program Structure Interface (PSI) to collect the data. In this case we’ll collect Anvil and Dagger Dependency Injection contributions and usage.

We’ll then continue on to use the collected analysis data to render a Compose report allowing us to see these dependency relationships that are unavailable to the IDE (because it is unaware of our dependency injection frameworks).

This talk gives you the foundation you need to write custom static analysis collectors, how you can explore that data, and solve problems for your team at scale.

Sam Edwards

May 24, 2024
Tweet

More Decks by Sam Edwards

Other Decks in Programming

Transcript

  1. First Task “As an Android developer, I want to know

    which Development Apps exercise the :impl Gradle Modules my team owns.”
  2. First Task “As an Android developer, I want to know

    which Development Apps exercise the :impl Gradle Modules my team owns.”
  3. First Task “As an Android developer, I want to know

    which Development Apps exercise the :impl Gradle Modules my team owns.”
  4. First Task “As an Android developer, I want to know

    which Development Apps exercise the :impl Gradle Modules my team owns.”
  5. Custom Gradle Plugin & Task • Con fi gure Project

    • Resolve all Transitive Dependencies • Execute Task • Report Output
  6. Kotlin Source Configurations • Runtime Con fi guration • Test

    Con fi guration • Release Con fi guration • Debug Con fi guration • Android Test Con fi guration
  7. Compose Web (WASM) - September 2023 • Wow, this is

    cool! • I can’t paste into the search inputs. • I can’t select the text from the results table. • This isn’t the right fi t…
  8. Compose HTML • Accessible from anywhere • Copy/Paste Support •

    Standalone, doesn’t require any backend (GitHub Pages) • Familiar to Kotlin developers • Dynamic Exploration
  9. Easy Questions Examples • Find the word “Analytics” in a

    fi le named *.kt? • Which fi les have the import “kotlin.test.Test”? Requirements • File contents indexed & searchable
  10. Hard Questions Examples • Is my code formatted correctly? •

    Did I follow our coding patterns in my latest commit? • Can you fi nd the usages of the Analytics interface? • What are the concrete implementations of the Analytics interface?
  11. Multiple Hard Questions 1. What are the concrete implementations of

    the Analytics interface? 2. What modules are they in? 3. Who are the owners of that module? 4. What fi les are they in?
  12. AST (Abstract Syntax Tree) • Kotlin PSI (Program Structure Interface)

    • Parses Kotlin Code into an AST Representation • Traversable
  13. Why? Missing Dagger Bindings > Task :app:kaptKotlin FAILED /Users/samedwards/Development/invert/examples/app/build/tmp/kapt3/ stubs/main/com/squareup/invert/examples/AppComponent.java:7:

    error: [Dagger/MissingBinding] com.squareup.invert.examples.di.NetworkGraph cannot be provided without an @Provides-annotated method. public abstract interface AppComponent { ^ Missing binding usage: com.squareup.invert.examples.di.NetworkGraph is requested at com.squareup.invert.examples.AppComponent.networking()
  14. Why? Missing Dagger Bindings > Task :app:kaptKotlin FAILED /Users/samedwards/Development/invert/examples/app/build/tmp/kapt3/ stubs/main/com/squareup/invert/examples/AppComponent.java:7:

    error: [Dagger/MissingBinding] com.squareup.invert.examples.di.NetworkGraph cannot be provided without an @Provides-annotated method. public abstract interface AppComponent { ^ Missing binding usage: com.squareup.invert.examples.di.NetworkGraph is requested at com.squareup.invert.examples.AppComponent.networking()
  15. Find DI Provides and Injects • Scan the Kotlin Source

    fi les for each module • Where is a type provided? • Where is a type injected? • Enable dynamic exploration of results
  16. Contributes @ContributesBinding(AppScope::class) class LiveNetworkGraph @Inject constructor( override val userRepo: UserRepo,

    override val categoryRepo: CategoryRepo, val networkConfig: NetworkConfig, val httpClient: HttpClient, ) : NetworkGraph {}
  17. Contributes @ContributesBinding(AppScope::class) class LiveNetworkGraph @Inject constructor( override val userRepo: UserRepo,

    override val categoryRepo: CategoryRepo, val networkConfig: NetworkConfig, val httpClient: HttpClient, ) : NetworkGraph {}
  18. Contributes @ContributesBinding(AppScope::class) class LiveNetworkGraph @Inject constructor( override val userRepo: UserRepo,

    override val categoryRepo: CategoryRepo, val networkConfig: NetworkConfig, val httpClient: HttpClient, ) : NetworkGraph {}
  19. Injects @ContributesBinding(AppScope::class) class LiveNetworkGraph @Inject constructor( override val userRepo: UserRepo,

    override val categoryRepo: CategoryRepo, val networkConfig: NetworkConfig, val httpClient: HttpClient, ) : NetworkGraph {}
  20. Injects @ContributesBinding(AppScope::class) class LiveNetworkGraph @Inject constructor( override val userRepo: UserRepo,

    override val categoryRepo: CategoryRepo, val networkConfig: NetworkConfig, val httpClient: HttpClient, ) : NetworkGraph {}
  21. Injects @ContributesBinding(AppScope::class) class LiveNetworkGraph @Inject constructor( override val userRepo: UserRepo,

    override val categoryRepo: CategoryRepo, val networkConfig: NetworkConfig, val httpClient: HttpClient, ) : NetworkGraph {}
  22. Injects @ContributesBinding(AppScope::class) class LiveNetworkGraph @Inject constructor( override val userRepo: UserRepo,

    override val categoryRepo: CategoryRepo, val networkConfig: NetworkConfig, val httpClient: HttpClient, ) : NetworkGraph {}
  23. Injects @ContributesBinding(AppScope::class) class LiveNetworkGraph @Inject constructor( override val userRepo: UserRepo,

    override val categoryRepo: CategoryRepo, val networkConfig: NetworkConfig, val httpClient: HttpClient, ) : NetworkGraph {}
  24. 🔃 Invert Playbook • Collector Plugin Ecosystem • Ability to

    create customized report pages • Available and Up-to-date on CI via GitHub Pages • Supports Deep Links