File tree Expand file tree Collapse file tree 4 files changed +51
-0
lines changed
compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea Expand file tree Collapse file tree 4 files changed +51
-0
lines changed Original file line number Diff line number Diff line change @@ -154,6 +154,26 @@ internal object StabilityAnalyzer {
154154 * PSI-based analysis (fallback for K1 mode or when K2 fails).
155155 */
156156 private fun analyzePsi (function : KtNamedFunction ): ComposableStabilityInfo {
157+ // Skip analysis for @NonRestartableComposable / @NonSkippableComposable —
158+ // these composables have no caching/comparison code, so stability is irrelevant.
159+ val hasNonRestartable = function.hasAnnotation(
160+ StabilityConstants .Strings .NON_RESTARTABLE_COMPOSABLE ,
161+ )
162+ val hasNonSkippable = function.hasAnnotation(
163+ StabilityConstants .Strings .NON_SKIPPABLE_COMPOSABLE ,
164+ )
165+ if (hasNonRestartable || hasNonSkippable) {
166+ return ComposableStabilityInfo (
167+ name = function.name ? : StabilityConstants .Strings .UNKNOWN ,
168+ fqName = function.fqName?.asString() ? : StabilityConstants .Strings .UNKNOWN ,
169+ isRestartable = ! hasNonRestartable,
170+ isSkippable = false ,
171+ isReadonly = function.hasAnnotation(StabilityConstants .Strings .READ_ONLY_COMPOSABLE ),
172+ parameters = emptyList(),
173+ receivers = emptyList(),
174+ )
175+ }
176+
157177 val parameters = function.valueParameters.mapNotNull { param ->
158178 analyzeParameter(param)
159179 }
Original file line number Diff line number Diff line change @@ -78,6 +78,7 @@ internal object StabilityConstants {
7878 const val UNKNOWN = " Unknown"
7979 const val COMPOSABLE = " Composable"
8080 const val NON_RESTARTABLE_COMPOSABLE = " NonRestartableComposable"
81+ const val NON_SKIPPABLE_COMPOSABLE = " NonSkippableComposable"
8182 const val READ_ONLY_COMPOSABLE = " ReadOnlyComposable"
8283 }
8384
Original file line number Diff line number Diff line change @@ -113,6 +113,8 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
113113 val allStable = allParams.all { it.stability == ParameterStability .STABLE }
114114
115115 val color = when {
116+ // Non-restartable / non-skippable composables have no stability semantics — show gray
117+ ! analysis.isSkippable && allParams.isEmpty() -> Color (0x80 , 0x80 , 0x80 )
116118 analysis.isSkippable && allStable -> Color (settings.stableGutterColorRGB)
117119 analysis.isSkippable -> Color (settings.stableGutterColorRGB)
118120 hasUnstable -> Color (settings.unstableGutterColorRGB)
@@ -149,6 +151,15 @@ public class StabilityLineMarkerProvider : LineMarkerProvider {
149151 isRuntimeOnly ->
150152 " 🟡 Runtime Stability (skippability determined at runtime)"
151153
154+ // Non-restartable / non-skippable with no params → annotated to skip caching
155+ ! analysis.isRestartable && totalCount == 0 ->
156+ " ⏭️ Non-Restartable (@NonRestartableComposable)" +
157+ " \n Stability analysis is not applicable."
158+
159+ analysis.isRestartable && ! analysis.isSkippable && totalCount == 0 ->
160+ " ⏭️ Non-Skippable (@NonSkippableComposable)" +
161+ " \n Stability analysis is not applicable."
162+
152163 analysis.isRestartable ->
153164 " ⚠️ Restartable (not skippable)"
154165
Original file line number Diff line number Diff line change @@ -74,6 +74,25 @@ internal object StabilityAnalyzerK2 {
7474 // Get function symbol
7575 val functionSymbol = function.symbol
7676
77+ // Skip analysis for @NonRestartableComposable / @NonSkippableComposable —
78+ // these composables have no caching/comparison code, so stability is irrelevant.
79+ val hasNonRestartable =
80+ function.hasAnnotation(StabilityConstants .Strings .NON_RESTARTABLE_COMPOSABLE )
81+ val hasNonSkippable =
82+ function.hasAnnotation(StabilityConstants .Strings .NON_SKIPPABLE_COMPOSABLE )
83+ if (hasNonRestartable || hasNonSkippable) {
84+ return ComposableStabilityInfo (
85+ name = function.name ? : StabilityConstants .Strings .UNKNOWN ,
86+ fqName = functionSymbol.callableId?.asSingleFqName()?.asString()
87+ ? : StabilityConstants .Strings .UNKNOWN ,
88+ isRestartable = ! hasNonRestartable,
89+ isSkippable = false ,
90+ isReadonly = function.hasAnnotation(StabilityConstants .Strings .READ_ONLY_COMPOSABLE ),
91+ parameters = emptyList(),
92+ receivers = emptyList(),
93+ )
94+ }
95+
7796 // Get the module containing this composable function (usage site)
7897 val usageSiteModule = ProjectFileIndex .getInstance(function.project).getModuleForFile(
7998 function.containingKtFile.virtualFile,
You can’t perform that action at this time.
0 commit comments