@@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project
2121import com.intellij.openapi.roots.ProjectRootManager
2222import com.intellij.openapi.vfs.VfsUtilCore
2323import com.intellij.psi.PsiManager
24+ import com.intellij.psi.util.PsiTreeUtil
2425import com.skydoves.compose.stability.idea.k2.StabilityAnalyzerK2
2526import com.skydoves.compose.stability.idea.settings.StabilityProjectSettingsState
2627import com.skydoves.compose.stability.idea.settings.StabilitySettingsState
@@ -35,7 +36,6 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
3536import org.jetbrains.kotlin.idea.caches.resolve.analyze
3637import org.jetbrains.kotlin.idea.caches.resolve.resolveMainReference
3738import org.jetbrains.kotlin.incremental.components.NoLookupLocation
38- import org.jetbrains.kotlin.nj2k.descendantsOfType
3939import org.jetbrains.kotlin.psi.KtClass
4040import org.jetbrains.kotlin.psi.KtFile
4141import org.jetbrains.kotlin.psi.KtNamedFunction
@@ -47,9 +47,10 @@ import org.jetbrains.kotlin.psi.KtUserType
4747import org.jetbrains.kotlin.resolve.BindingContext
4848import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
4949import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
50+ import org.jetbrains.kotlin.types.AbbreviatedType
5051import org.jetbrains.kotlin.types.KotlinType
5152import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
52- import java.lang.reflect.InvocationTargetException
53+ import java.util.concurrent.ConcurrentHashMap
5354
5455/* *
5556 * Helper class to hold stability result with reason.
@@ -81,6 +82,14 @@ internal object StabilityAnalyzer {
8182 private val ignoredPatterns: List <Regex >
8283 get() = settings.getIgnoredPatternsAsRegex()
8384
85+ /* *
86+ * Cache for project-wide typealias lookups to avoid repeated filesystem scans.
87+ * Key: "aliasName|fqNameHint", Value: Pair(result, timestampMs).
88+ * Entries older than [TYPE_ALIAS_CACHE_TTL_MS] are considered stale.
89+ */
90+ private val typeAliasCache = ConcurrentHashMap <String , Pair <KtTypeAlias ?, Long >>()
91+ private const val TYPE_ALIAS_CACHE_TTL_MS = 30_000L // 30 seconds
92+
8493 @get:TestOnly
8594 @set:TestOnly
8695 internal var resolveMainReferenceOverride: ((KtReferenceExpression ) -> Any? )? = null
@@ -692,8 +701,7 @@ internal object StabilityAnalyzer {
692701 val ktFile = typeRef.containingKtFile
693702
694703 // 1) File-local scan (fast, no IO)
695- ktFile.declarations
696- .descendantsOfType<KtTypeAlias >()
704+ PsiTreeUtil .findChildrenOfType(ktFile, KtTypeAlias ::class .java)
697705 .firstOrNull { it.name == aliasName }
698706 ?.let { return it }
699707
@@ -714,6 +722,26 @@ internal object StabilityAnalyzer {
714722 project : Project ,
715723 aliasName : String ,
716724 fqNameHint : String? ,
725+ ): KtTypeAlias ? {
726+ val cacheKey = " $aliasName |$fqNameHint "
727+ val now = System .currentTimeMillis()
728+
729+ // Check cache first
730+ typeAliasCache[cacheKey]?.let { (cached, timestamp) ->
731+ if (now - timestamp < TYPE_ALIAS_CACHE_TTL_MS && cached?.isValid != false ) {
732+ return cached
733+ }
734+ }
735+
736+ val result = findTypeAliasInProjectUncached(project, aliasName, fqNameHint)
737+ typeAliasCache[cacheKey] = result to now
738+ return result
739+ }
740+
741+ private fun findTypeAliasInProjectUncached (
742+ project : Project ,
743+ aliasName : String ,
744+ fqNameHint : String? ,
717745 ): KtTypeAlias ? {
718746 val psiManager = PsiManager .getInstance(project)
719747 val packageHint = fqNameHint?.substringBeforeLast(' .' , " " )
@@ -735,8 +763,7 @@ internal object StabilityAnalyzer {
735763 return @iterateChildrenRecursively true
736764 }
737765
738- val alias = psi.declarations
739- .descendantsOfType<KtTypeAlias >()
766+ val alias = PsiTreeUtil .findChildrenOfType(psi, KtTypeAlias ::class .java)
740767 .firstOrNull { it.name == aliasName }
741768 ? : return @iterateChildrenRecursively true
742769
@@ -1217,33 +1244,7 @@ internal fun KtNamedFunction.hasAnnotation(shortName: String): Boolean {
12171244 */
12181245
12191246private fun KotlinType.expandTypeAliasIfNeeded (): KotlinType {
1220- val abbreviatedTypeClass = try {
1221- Class .forName(" org.jetbrains.kotlin.types.AbbreviatedType" )
1222- } catch (_: ClassNotFoundException ) {
1223- return this
1224- } catch (_: NoClassDefFoundError ) {
1225- return this
1226- } catch (_: LinkageError ) {
1227- return this
1228- }
1229-
1230- if (! abbreviatedTypeClass.isInstance(this )) return this
1231-
1232- val getExpanded = abbreviatedTypeClass.methods.firstOrNull {
1233- it.name == " getExpandedType" && it.parameterCount == 0
1234- } ? : return this
1235-
1236- return try {
1237- (getExpanded.invoke(this ) as ? KotlinType ) ? : this
1238- } catch (_: IllegalAccessException ) {
1239- this
1240- } catch (_: IllegalArgumentException ) {
1241- this
1242- } catch (_: InvocationTargetException ) {
1243- this
1244- } catch (_: ClassCastException ) {
1245- this
1246- }
1247+ return if (this is AbbreviatedType ) expandedType else this
12471248}
12481249
12491250/* *
0 commit comments