Skip to content

Commit 05040d1

Browse files
authored
Merge pull request #65 from skydoves/fix/genertic-type
Inference generic types
2 parents e94b9a1 + 647a668 commit 05040d1

File tree

2 files changed

+86
-2
lines changed

2 files changed

+86
-2
lines changed

app/src/main/kotlin/com/skydoves/myapplication/MainActivity.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,19 @@ class MainActivity : ComponentActivity() {
7474
}
7575
}
7676

77+
data class UiResult<T>(
78+
val data: T,
79+
val isPending: Boolean,
80+
)
81+
82+
@Composable
83+
fun MyComposable(
84+
uiResult: UiResult<Unit>,
85+
uiResult2: UiResult<UnstableUser>,
86+
uiResult4: UiResult<List<String>>,
87+
) {
88+
}
89+
7790
/**
7891
* Main screen composable.
7992
* This should be skippable as it has no parameters.

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

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,79 @@ internal class KtStabilityInferencer(private val project: Project? = null) {
181181
)
182182
}
183183

184-
// Analyze with empty tracking set
185-
return analyzeClassSymbol(classSymbol, emptySet())
184+
// Analyze the class itself
185+
val classStability = analyzeClassSymbol(classSymbol, emptySet())
186+
187+
// Analyze type arguments if present (e.g., UiResult<Unit> -> check if Unit is stable)
188+
val typeArgumentStabilities = analyzeTypeArguments(nonNullableType)
189+
190+
// If class is already unstable, return it directly
191+
if (classStability.isUnstable()) {
192+
return classStability
193+
}
194+
195+
// If any type argument is unstable, the whole type is unstable
196+
val unstableTypeArg = typeArgumentStabilities.find { it.isUnstable() }
197+
if (unstableTypeArg != null) {
198+
return KtStability.Certain(
199+
stable = false,
200+
reason = "Has unstable type argument: ${unstableTypeArg.getReasonString()}",
201+
)
202+
}
203+
204+
// If any type argument is runtime, the whole type is runtime
205+
val runtimeTypeArg = typeArgumentStabilities.find { it is KtStability.Runtime }
206+
if (runtimeTypeArg != null) {
207+
return KtStability.Runtime(
208+
className = originalTypeString,
209+
reason = "Has runtime type argument: ${runtimeTypeArg.getReasonString()}",
210+
)
211+
}
212+
213+
// If class stability is runtime but all type args are stable, still runtime
214+
if (classStability is KtStability.Runtime) {
215+
return classStability
216+
}
217+
218+
// All stable - return class stability with type args info if applicable
219+
val hasStableTypeArgs = typeArgumentStabilities.isNotEmpty() &&
220+
typeArgumentStabilities.all { it.isStable() }
221+
return if (hasStableTypeArgs) {
222+
KtStability.Certain(
223+
stable = true,
224+
reason = "${classStability.getReasonString()} (all type arguments are stable)",
225+
)
226+
} else {
227+
classStability
228+
}
229+
}
230+
231+
/**
232+
* Analyzes type arguments of a generic type.
233+
* Returns empty list if type has no type arguments.
234+
*/
235+
context(KaSession)
236+
private fun analyzeTypeArguments(type: KaType): List<KtStability> {
237+
return try {
238+
// Use reflection to access typeArguments as it may differ between K2 versions
239+
val typeArgsMethod = type::class.members.find { it.name == "typeArguments" }
240+
if (typeArgsMethod != null) {
241+
@Suppress("UNCHECKED_CAST")
242+
val typeArgs = typeArgsMethod.call(type) as? List<*> ?: return emptyList()
243+
typeArgs.mapNotNull { typeArg ->
244+
// Each type argument is a KaTypeProjection which has a type property
245+
val typeProperty = typeArg?.let { arg ->
246+
arg::class.members.find { it.name == "type" }?.call(arg) as? KaType
247+
}
248+
typeProperty?.let { ktStabilityOf(it) }
249+
}
250+
} else {
251+
emptyList()
252+
}
253+
} catch (e: Exception) {
254+
// If we can't analyze type arguments, return empty
255+
emptyList()
256+
}
186257
}
187258

188259
/**

0 commit comments

Comments
 (0)