Skip to content

Stability configuration files in compiler plugin#181

Open
mlewandowskiphysitrack wants to merge 12 commits into
skydoves:mainfrom
mlewandowskiphysitrack:stability-configuration-files-in-compiler-plugin
Open

Stability configuration files in compiler plugin#181
mlewandowskiphysitrack wants to merge 12 commits into
skydoves:mainfrom
mlewandowskiphysitrack:stability-configuration-files-in-compiler-plugin

Conversation

@mlewandowskiphysitrack

@mlewandowskiphysitrack mlewandowskiphysitrack commented Jul 5, 2026

Copy link
Copy Markdown

🎯 Goal

The main goal is to fix #176. To achieve this, stability configuration files are passed directly to compiler plugin and their contents are taken into account there when checking stability of classes used as parameters in Composables.

🛠 Implementation details

To achieve this, new option for passing stability configuration files is added to compiler plugin. Compiler plugin parses stability configuration files (logic copied from existing, buggy stability configuration files handling in Gradle plugin) and StabilityAnalyzerTransformer returns ParameterStability.STABLE if class fully-qualified name matches entry in stability configuration file. There is a parity between this and checking user settings (ignored types, custom stable types) in KtStabilityInferencer.

In Gradle plugin, there is added new option stabilityConfigurationFiles on top-level extension. This is done for backwards compatibility, but also to indicate that the passed files are not wired into stability validation functionality in Gradle plugin, but go directly to the compiler plugin (similarly to e.g. traceAll option) and are used by all features that use compiler plugin work (e.g. mentioned tracing). The old option in stabilityValidation block is deprecated and a subject to removal in some future release. It is not removed to not suddenly break compilation when someone bumps the plugin from 0.10.0 to 0.11.0. The changes are reflected in the docs.

The change is tested by IR compiler test - see commit message and comment in the test that explain why this was the only reasonable way to test if the feature behaves correctly.

✍️ Explain examples

// stability_config.conf
ToBeStable
// ComposableToBeSkippable.kt
import androidx.compose.runtime.Composable

interface ToBeStable

data class Wrapper(val value: ToBeStable)

@Composable
fun ComposableToBeSkippable(wrapper: Wrapper) {
  println(wrapper.toString())
}

As explained in #176, when attaching stability_config.conf via stabilityValidation block option, when running stabilityCheck the ComposableToBeSkippable would not be marked as skippable, and Wrapper would be marked as UNSTABLE. When attaching via new option on top-level extension, the correct value of skippable/stable will be returned.

Migration path

Before:

composeStabilityAnalyzer {
    stabilityValidation {
        ...
        stabilityConfigurationFiles.add( // <--- this is now deprecated
            rootProject.layout.projectDirectory.file("stability_config.conf")
        )
    }
}

After:

composeStabilityAnalyzer {
    stabilityValidation {
        ...
    }
    stabilityConfigurationFiles.add( // <--- correct, non-deprecated way
        rootProject.layout.projectDirectory.file("stability_config.conf")
    )
}

Summary by CodeRabbit

  • New Features

    • Added support for stability configuration files to mark additional types as stable via compiler options or Gradle configuration.
    • Added support for multiple configuration files and wildcard-based type matching (including * and ** patterns).
  • Documentation

    • Added a new guide: “Stability configuration files”.
    • Updated Gradle plugin docs to deprecate the older nested option in favor of the top-level configuration.
  • Bug Fixes

    • Improved stability inference so configured types are treated as stable, with clearer reporting for config-driven stability.
  • Tests

    • Added IR dump test fixtures covering matching, mismatches, and no-config behavior.

To be used in StabilityAnalyzerIrGenerationExtension
… to compiler plugin and pass it down to IrGenerationExtension
…rs in analyzer to mark classes specified in stability configuration files as stable
…nfig sub-extension; stabilityConfigurationFiles in StabilityAnalyzerExtension should be used instead, as it wires directly into compiler plugin.
…tion block, point to and describe stabilityConfigurationFiles in composeStabilityAnalyzer block (that applies directly to compiler plugin)
… into account

Using IR test with ENABLE_TRACE_ALL for this as it's the only way to get a verdict on stability in existing tests setup.
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 47b3c3d4-031d-4f2c-9668-2a24a1047402

📥 Commits

Reviewing files that changed from the base of the PR and between f6df6d3 and fd97a5a.

📒 Files selected for processing (2)
  • stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityAnalyzerIrGenerationExtension.kt
  • stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityConfigParser.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityAnalyzerIrGenerationExtension.kt
  • stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityConfigParser.kt

📝 Walkthrough

Walkthrough

Adds stability configuration file support through parser, compiler/Gradle wiring, transformer classification, and validation fixtures/docs.

Changes

Stability configuration file feature

Layer / File(s) Summary
Config parser and matcher
stability-compiler/.../StabilityConfigParser.kt
Parses config files or line lists into matchers, validates patterns, supports */**, and matches type names after stripping generics and nullable suffixes.
Compiler CLI and IR extension wiring
stability-compiler/.../StabilityAnalyzerCommandLineProcessor.kt, StabilityAnalyzerPluginRegistrar.kt, StabilityAnalyzerIrGenerationExtension.kt
Adds the compiler option and config key, forwards configured file lists through registrar wiring, and converts config files into matchers for the IR transformer.
Transformer stability classification
stability-compiler/.../lower/StabilityAnalyzerTransformer.kt
Treats matched types as stable, updates the analysis order comment, and reports a dedicated stability reason for config-file matches.
Gradle extension and plugin wiring
stability-gradle/.../StabilityAnalyzerExtension.kt, StabilityAnalyzerGradlePlugin.kt
Adds the top-level stabilityConfigurationFiles property, deprecates the nested validation property, and emits one compiler option per configured file.
Test fixtures and docs
compiler-tests/.../StabilityTestDirectives.kt, StabilityTestConfigurator.kt, dump/ir/StabilityConfigurationFile*.kt, stability_config*.conf, docs/gradle-plugin/*, mkdocs.yml
Adds test directive wiring, IR dump fixtures and config files, documentation for the new setting, deprecation notes, and navigation entry updates.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: compiler plugin, Stability Validation

Suggested reviewers: skydoves

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: adding stability configuration file support in the compiler plugin.
Description check ✅ Passed The description covers the goal, implementation details, examples, and migration path, but omits the template’s review-prep section.
Linked Issues check ✅ Passed The changes address #176 by parsing stability config files in the compiler path and using them to mark wrapped parameter types stable.
Out of Scope Changes check ✅ Passed The added docs, tests, and deprecation path are all aligned with the stability-configuration-file feature and its migration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment on lines -472 to +474
* 4. Known stable types
* 4. Known stable types and stability configuration files types

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extending the existing point since there's a mismatch in the numbering; and to not spoil the diff with numbering changes.

valueDescription = "<path>",
description = "Stability configuration file to take into account when analyzing stability",
required = false,
allowMultipleOccurrences = true,

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to pass multiple files

""".trimIndent()
}

public class FqNameMatcher(private val pattern: String) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class needs to be public since it's used in public constructor of public StabilityAnalyzerIrGenerationExtension

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@compiler-tests/src/test/data/dump/ir/StabilityConfigurationFile.kt`:
- Around line 1-17: Add directive-driven coverage around
StabilityConfigurationFile to exercise the skip/exclusion matrix for the new
stability config behavior. Keep the existing positive case in
ComposableToBeSkippable, and add a companion test using the same IR dump pattern
(or a nearby test in the same suite) that shows a type not listed in
stability_config.conf remains unstable, plus a baseline case without
STABILITY_CONFIGURATION_FILES to confirm default behavior is unchanged.
Reference the existing markers in StabilityConfigurationFile and the
ComposableToBeSkippable/Wrapper/ToBeStable setup so the new test clearly
distinguishes configured vs non-configured stability.
- Line 3: The current StabilityConfigurationFile fixture only exercises the
configured stable path, so add a companion negative test that either removes
STABILITY_CONFIGURATION_FILES or points to a config that does not include
ToBeStable. Update the relevant IR dump test setup alongside
StabilityConfigurationFile so the skip/unstable branch is covered and the
behavior is verified with a missing or mismatched stability config.

In
`@stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityAnalyzerIrGenerationExtension.kt`:
- Around line 60-64: Wrap the stability config parsing in
StabilityAnalyzerIrGenerationExtension.generate the same way as the dependency
scan so a bad config file does not abort compilation. Update the
stabilityConfigurationFiles processing around
StabilityConfigParser.fromFile(...) to catch parsing/read errors per file, skip
only the failing file, and continue building stabilityConfigurationMatchers from
the remaining valid configs.

In
`@stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityConfigParser.kt`:
- Around line 50-99: StabilityConfigParser.fromFile() still lets read/parse
exceptions escape from StabilityConfigParserImpl and abort compilation; make it
fail open by catching file read and parser construction errors in fromFile and
returning StabilityConfigParserImpl(emptyList()) instead. Keep the existing
parsing logic in StabilityConfigParserImpl, but ensure any error while reading
the file or parsing lines falls back to no stableTypeMatchers so
StabilityAnalyzerIrGenerationExtension.generate() can skip instrumentation
instead of failing.
- Around line 172-202: The generic mask is parsed in StabilityConfigParser but
never applied during matching, so patterns like foo.Bar<*,_> behave the same as
foo.Bar. Update FqNameMatcher to use the parsed mask when evaluating matches,
and ensure the logic in matches(name) validates generic arguments against mask
instead of only comparing the raw key/regex. Apply the same fix in the mirrored
stability-gradle parser copy so both implementations honor generic-mask syntax
consistently.

In
`@stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerExtension.kt`:
- Around line 282-288: The deprecation message in
StabilityAnalyzerExtension.stabilityConfigurationFiles is missing a space
because the two string literals are concatenated directly. Update the
`@Deprecated` message to read naturally by adding the missing space between
“option” and “in top-level StabilityAnalyzerExtension” so the generated
deprecation text is correct.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2319d9a7-efa0-4731-b1fe-af20e9028872

📥 Commits

Reviewing files that changed from the base of the PR and between 7f68d4c and 3e6d7cd.

⛔ Files ignored due to path filters (4)
  • compiler-tests/src/test/data/dump/ir/StabilityConfigurationFile.fir.kt.txt is excluded by !compiler-tests/src/test/data/**/*.txt
  • compiler-tests/src/test/java/com/skydoves/compose/stability/compiler/tests/IrDumpTestGenerated.java is excluded by !compiler-tests/src/test/java/**
  • stability-compiler/api/stability-compiler.api is excluded by !**/api/*.api
  • stability-gradle/api/stability-gradle.api is excluded by !**/api/*.api
📒 Files selected for processing (14)
  • compiler-tests/src/test/data/dump/ir/StabilityConfigurationFile.kt
  • compiler-tests/src/test/data/dump/ir/stability_config.conf
  • compiler-tests/src/test/kotlin/com/skydoves/compose/stability/compiler/tests/StabilityTestConfigurator.kt
  • compiler-tests/src/test/kotlin/com/skydoves/compose/stability/compiler/tests/StabilityTestDirectives.kt
  • docs/gradle-plugin/stability-configuration-files.md
  • docs/gradle-plugin/stability-validation.md
  • mkdocs.yml
  • stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityAnalyzerCommandLineProcessor.kt
  • stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityAnalyzerIrGenerationExtension.kt
  • stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityAnalyzerPluginRegistrar.kt
  • stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/StabilityConfigParser.kt
  • stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/lower/StabilityAnalyzerTransformer.kt
  • stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerExtension.kt
  • stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt

Comment thread compiler-tests/src/test/data/dump/ir/StabilityConfigurationFile.kt
Comment thread compiler-tests/src/test/data/dump/ir/StabilityConfigurationFile.kt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Data classes that contain classes defined in stability configuration file incorrectly mark Composable as unstable

1 participant