Skip to content

1.5.0

Latest

Choose a tag to compare

@ZacSweers ZacSweers released this 26 Oct 05:18
· 14 commits to main since this release

New

  • [metrox-viewmodel-compose] Add support for linuxX64.
  • [FIR] Add the diagnostic-level compiler option to change the severity of Metro diagnostics reported in FIR. It works like kotlinc's -Xwarning-level but can also lower errors and use IDE-only levels. See changing diagnostic severity.
  • [FIR/IC] Add the function-inject-annotations-override compiler option to use dedicated annotations for top-level function injection. See custom function injection annotations.
  • [FIR/IR] Support context parameters and extension receivers on @Provides functions and properties. Both are dependencies and can be qualified.
  • [Gradle] Add opt-in checks to find Metro contributions hidden from a graph's compile classpath. Run checkMainMetroHiddenDependencies (i.e., on CI). It writes a report and fails if it finds hidden contributions.
    • Supports JVM and Android compilations, including JVM targets in KMP. See checking for hidden contributions.
    • Also checks Hilt aggregation metadata when Hilt interop is enabled.
    • Configure scope filters with scopes in the experimental metro.hiddenDependencies DSL. Enable checkOnCompile in the same block to run each check automatically after its Kotlin compilation, including when compilation fails. Automatic checks are disabled by default and may become the default in a future release.

Enhancements

  • [IR] Reduce generated code in graph extensions by reusing existing parent bindings when their contributions and resolved dependencies are unchanged. Objects constructed in a single extension can receive sets and maps from parent getters when the parent already uses those collections.
  • [IR] Graphs no longer generate a private getter for a multibinding that only one site reads. That site builds the collection itself.
    • Reads from graph extensions that reuse the multibinding count too.
    • Multibindings with more than 25 contributions also keep a getter so they don't crowd their reader's method. You can configure that threshold with the new multibinding-getter-threshold compiler option.
  • [IR] When several graph extensions use the same binding, their nearest common ancestor now generates its construction code in a shared getter. This also applies when different classes in those extensions depend on the binding. Unscoped bindings still create a new instance on each call.
  • [IR] When several accessors request the same key and return type, the first accessor holds the binding's code and the others call through to it.
  • [IR] Graphs no longer store themselves in a field. Code that needs the graph reads this, or the graph reference that a shard or switching provider already holds.
  • [IR] Generated code wraps instances in providers with providerOf(). On JVM that's one static call. InstanceFactory.invoke() also loaded its companion and boxed the value class.
  • [IR] Provider lambdas that Metro generates are SAM-converted to Provider directly. The inline provider() function they went through before added a type check and a second wrapper object at every site.
  • [IR] Each switching provider class numbers its bindings from zero. Shards used to switch on graph-wide IDs with gaps between them.
  • [IR] Switching provider branches read the graph reference into a local when more than one branch needs it.
  • [IR] Graph shard constructors read the graph from their constructor parameter. A shard drops its graph field when only its constructor used it.
  • [IR] Instance reads of @Includes graph accessors that return Provider<T> or () -> T call the accessor directly. They used to cache a converted provider in a field or wrap the accessor in a new provider lambda. Lazy accessors still cache their handle, so their value is computed once.
  • [IR/JVM] Fields in generated graphs, shards, and switching providers are now package-private on JVM. This reduces synthetic accessors.
  • [IR/JVM] Generated code calls @JvmStatic functions in objects and companion objects statically on JVM. This keeps companion classes out of the caller's constant pool. At runtime each call still goes through the companion, since the static method forwards to it. In a 500-module benchmark this made the generated graph classes 17% smaller.
  • [IR/JS] On JS, converting a Provider to () -> T or suspend () -> T now calls a small runtime helper. Each site used to generate its own wrapper lambda.
  • [IR/interop] Providers of absent @BindsOptionalOf bindings use an InstanceFactory that holds Optional.empty() instead of generating a local lambda each time.

Fixes

  • [FIR] Reduce redundant lookups when discovering contributed graph extension factories.
  • [FIR] Fix accessor override status on Kotlin 2.5.0-dev-10106.
  • [FIR/IC] Avoid unnecessary recompilation of independent injected classes when generating contribution hints.
  • [FIR/interop] Fix duplicate class errors during caused by Hilt interop generating hints for upstream modules.
  • [FIR/IR/interop] Fix incremental builds not updating graphs when an upstream Hilt @AggregatedDeps marker changes or is removed.
  • [FIR/IR/Gradle] Fix false self-loop errors in analyzeMetroGraph when one qualified map depends on another.
  • [IR] Fix a qualified request resolving to an unqualified @Inject class. A qualified optional binding now uses its default, and other qualified requests report a missing binding.
  • [IR] Fix member injection across modules when an injected member uses a generic base class's type parameter.
  • [IR] Fix missing binding errors for accessors inherited from another module having no source location.
  • [IR] Fix MissingRuntimeCoroutines errors for injected top-level functions pointing at 0:0 in a generated file. They're now reported on the function.
  • [IR] Fix declaration parents for copied default argument lambdas and callable references, including references inside generated provider wrappers.
  • [IR] Include extension receivers in generated lambda function types, including set and map builder callbacks.
  • [IR] Fix a compiler crash when injecting a provider of a single-entry Map<Class<*>, V> with enableKClassToClassMapKeyInterop enabled.
  • [IR] Fix IR diagnostic reporting on Kotlin 2.5.0-dev-10106.
  • [IR] Fix graphs storing an @Includes graph parameter in two fields when the included graph has graph extensions.
  • [IR] Fix runtime tracing passing a contextual type that matches the binding's type. It's now null as documented.
  • [IR/IC] Fix stale graphs in incremental builds when a replacing binding container's @ContributesTo annotation is removed and restored.
  • [IR/native/circuit] Fix an IrLinkageError in generated SubCircuit UI factories on Kotlin/Native with Kotlin 2.5.0-Beta1.
  • [compat] Fix Kotlin 2.5.0-Beta1 selecting a compat implementation built for newer 2.5.0 dev builds. Kotlin Beta and RC releases now map to the dev compat implementation that matches their branch point.
  • [metrox-android] Fix MetroAppComponentFactory crashing with a ClassCastException when Android starts the app with a plain Application for a full backup or restore.

Changes

  • Build against Android SDK 37 and AGP 9.1.1.
  • Update Lifecycle dependencies to 2.11.0 and JetBrains Compose to 1.12.2.
  • [metrox-viewmodel/metrox-viewmodel-compose] Drop Apple x86_64 targets removed by upstream dependencies. metrox-viewmodel drops iosX64, macosX64, tvosX64, and watchosX64. metrox-viewmodel-compose drops iosX64 and macosX64.
  • Update androidx.annotation to 1.11.0 and androidx.core to 1.19.1.
  • [compat] Test against Kotlin 2.5.0-dev-10106.
  • [FIR] Report a @MapKey on a member with no binding annotation as a dedicated MAP_KEY_WITHOUT_INTO_MAP error. If another tool binds the member into a map, lower it with the diagnostic-level compiler option or @Suppress. Binding declarations like @Provides keep the existing error.
  • [FIR/interop] Report a @MapKey on a class with no Metro contribution annotation as a dedicated MAP_KEY_WITHOUT_CONTRIBUTES_INTO_MAP error. If another tool binds the class into a map, lower it to a warning with the diagnostic-level compiler option.
  • [FIR/IR/IC] Enable omit-redundant-mirrors by default on Kotlin 2.4.0 and newer to omit generated declaration mirrors when compiler metadata and declaration finders provide the same information. It remains disabled by default on older Kotlin versions. Disable it with compilerOptions.disable("omit-redundant-mirrors") in the metro DSL.
  • [Gradle] enableTopLevelFunctionInjection is disabled by default for now on all Kotlin versions because it makes incremental builds recompile more files. If you use top-level function injection, enable it with enableTopLevelFunctionInjection.set(true) in the metro DSL. See incremental compilation perf docs.
  • Test Android Studio Rabbit 1 (2026.2.1.8).
  • Test IntelliJ 2026.2.3.
  • Update to androidx.tracing 2.0.4.
  • Test against Kotlin 2.5.0-Beta2.

Contributors

Special thanks to the following contributors for contributing to this release!

Consider sponsoring Metro's development

What's Changed

New Contributors

Full Changelog: 1.4.5...1.5.0