Skip to content

Commit f8b5a8e

Browse files
authored
Skip analysing for specific compose annotation (#111)
1 parent b85adf7 commit f8b5a8e

File tree

4 files changed

+51
-0
lines changed

4 files changed

+51
-0
lines changed

compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/StabilityAnalyzer.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff 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
}

compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/StabilityConstants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff 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

compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/StabilityLineMarkerProvider.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff 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+
"\nStability analysis is not applicable."
158+
159+
analysis.isRestartable && !analysis.isSkippable && totalCount == 0 ->
160+
"⏭️ Non-Skippable (@NonSkippableComposable)" +
161+
"\nStability analysis is not applicable."
162+
152163
analysis.isRestartable ->
153164
"⚠️ Restartable (not skippable)"
154165

compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/k2/StabilityAnalyzerK2.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff 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,

0 commit comments

Comments
 (0)