Skip to content

Commit 6eaf826

Browse files
authored
Merge pull request #13 from skydoves/fix/stability-parcelable
Inference logics for Parcelable classes
2 parents a0e9a5c + fc177b2 commit 6eaf826

File tree

3 files changed

+74
-11
lines changed

3 files changed

+74
-11
lines changed

compose-stability-analyzer-idea/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ All notable changes to the IntelliJ IDEA plugin will be documented in this file.
77
### Fixed
88
- Fixed stability analysis for Compose shape types (RoundedCornerShape, CircleShape, etc.) to correctly show as STABLE instead of RUNTIME
99
- Fixed StackOverflowError when analyzing recursive or self-referential types (Issue #11)
10+
- Fixed false positive warnings for @Parcelize data classes (Issue #3)
1011
- Improved consistency between IDEA plugin and compiler plugin stability inference
1112
- Added Compose Foundation shapes to known stable types list
1213
- Added cycle detection to prevent infinite recursion in type stability analysis
1314

1415
### Improved
1516
- Enhanced accuracy of stability analysis to match compiler plugin behavior
1617
- Better handling of complex function type aliases and deeply nested generic types
18+
- @Parcelize-annotated classes with stable properties are now correctly identified as STABLE
1719

1820
---
1921

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

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ import org.jetbrains.kotlin.analysis.api.types.KaTypeNullability
4848
* 14. Standard collections (RUNTIME)
4949
* 15. Value classes
5050
* 16. Enums
51-
* 17. Interfaces (RUNTIME)
52-
* 18. Abstract classes (RUNTIME)
53-
* 19. Regular classes - property analysis (returns STABLE/UNSTABLE if definitive)
54-
* 20. @StabilityInferred (RUNTIME - only for uncertain cases)
51+
* 17. @Parcelize - check properties
52+
* 18. Interfaces (RUNTIME)
53+
* 19. Abstract classes (RUNTIME)
54+
* 20. Regular classes - property analysis (returns STABLE/UNSTABLE if definitive)
55+
* 21. @StabilityInferred (RUNTIME - only for uncertain cases)
5556
*/
5657
internal class KtStabilityInferencer {
5758

@@ -301,23 +302,54 @@ internal class KtStabilityInferencer {
301302
return KtStability.Certain(stable = true, reason = StabilityConstants.Messages.ENUM_STABLE)
302303
}
303304

304-
// 17. Interfaces - cannot determine (RUNTIME)
305+
// 17. @Parcelize data classes - check only properties, ignore Parcelable interface
306+
val hasParcelize = classSymbol.annotations.any { annotation ->
307+
annotation.classId?.asSingleFqName()?.asString() == "kotlinx.parcelize.Parcelize"
308+
}
309+
if (hasParcelize) {
310+
val properties = classSymbol.declaredMemberScope.callables
311+
.filterIsInstance<KaPropertySymbol>()
312+
.toList()
313+
314+
// Check for var properties
315+
if (properties.any { !it.isVal }) {
316+
return KtStability.Certain(
317+
stable = false,
318+
reason = "Has mutable (var) properties",
319+
)
320+
}
321+
322+
// Check property type stability
323+
val allPropertiesStable = properties.all { property ->
324+
val propertyStability = ktStabilityOf(property.returnType)
325+
propertyStability.isStable()
326+
}
327+
328+
if (allPropertiesStable) {
329+
return KtStability.Certain(
330+
stable = true,
331+
reason = "@Parcelize with all stable properties",
332+
)
333+
}
334+
}
335+
336+
// 18. Interfaces - cannot determine (RUNTIME)
305337
if (classSymbol.classKind == KaClassKind.INTERFACE) {
306338
return KtStability.Runtime(
307339
className = fqName ?: simpleName,
308340
reason = "Interface type - actual implementation could be mutable",
309341
)
310342
}
311343

312-
// 18. Abstract classes - cannot determine (RUNTIME)
344+
// 19. Abstract classes - cannot determine (RUNTIME)
313345
if (classSymbol.modality == KaSymbolModality.ABSTRACT) {
314346
return KtStability.Runtime(
315347
className = fqName ?: simpleName,
316348
reason = "Abstract class - actual implementation could be mutable",
317349
)
318350
}
319351

320-
// 19. Regular classes - analyze properties first before checking @StabilityInferred
352+
// 20. Regular classes - analyze properties first before checking @StabilityInferred
321353
val propertyStability = analyzeClassProperties(classSymbol, currentlyAnalyzing)
322354

323355
return when {

stability-compiler/src/main/kotlin/com/skydoves/compose/stability/compiler/lower/StabilityAnalyzerTransformer.kt

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ public class StabilityAnalyzerTransformer(
234234
* 11. Standard collections - RUNTIME
235235
* 12. Value classes (inline classes)
236236
* 13. Enums - STABLE
237-
* 14. @StabilityInferred - RUNTIME
237+
* 14. @Parcelize - check properties
238238
* 15. Interfaces - RUNTIME
239239
* 16. Abstract classes - RUNTIME
240240
* 17. Regular classes (with property analysis)
@@ -339,17 +339,46 @@ public class StabilityAnalyzerTransformer(
339339
return ParameterStability.STABLE
340340
}
341341

342-
// 14. Interfaces - cannot determine (RUNTIME)
342+
// 14. @Parcelize data classes - check only properties, ignore Parcelable interface
343+
if (clazz.hasAnnotation(FqName("kotlinx.parcelize.Parcelize"))) {
344+
val properties = clazz.declarations
345+
.filterIsInstance<org.jetbrains.kotlin.ir.declarations.IrProperty>()
346+
347+
if (properties.isEmpty()) {
348+
return ParameterStability.STABLE
349+
}
350+
351+
// Check for var properties
352+
if (properties.any { it.isVar }) {
353+
return ParameterStability.UNSTABLE
354+
}
355+
356+
// Check property type stability
357+
val propertyStabilities = properties.mapNotNull { property ->
358+
property.getter?.returnType?.let { analyzeTypeStability(it) }
359+
}
360+
361+
if (propertyStabilities.any { it == ParameterStability.UNSTABLE }) {
362+
return ParameterStability.UNSTABLE
363+
}
364+
365+
if (propertyStabilities.all { it == ParameterStability.STABLE }) {
366+
return ParameterStability.STABLE
367+
}
368+
// If properties have mixed stability, fall through to interface check
369+
}
370+
371+
// 15. Interfaces - cannot determine (RUNTIME)
343372
if (clazz.isInterfaceIr()) {
344373
return ParameterStability.RUNTIME
345374
}
346375

347-
// 15. Abstract classes - cannot determine (RUNTIME)
376+
// 16. Abstract classes - cannot determine (RUNTIME)
348377
if (clazz.modality == org.jetbrains.kotlin.descriptors.Modality.ABSTRACT) {
349378
return ParameterStability.RUNTIME
350379
}
351380

352-
// 16. Regular classes - analyze properties first before checking @StabilityInferred
381+
// 17. Regular classes - analyze properties first before checking @StabilityInferred
353382
val propertyStability = analyzeClassProperties(clazz)
354383

355384
when (propertyStability) {

0 commit comments

Comments
 (0)