Added fallback for LightClasses getOwnFields, getOwnMethods if dummy resolve failed (KT-27243, KT-33561)
This commit is contained in:
+11
-2
@@ -64,6 +64,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl
|
||||
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
|
||||
@@ -81,9 +82,9 @@ import org.jetbrains.kotlin.utils.sure
|
||||
|
||||
class IDELightClassConstructionContext(
|
||||
bindingContext: BindingContext, module: ModuleDescriptor,
|
||||
langaugeVersionSettings: LanguageVersionSettings,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
val mode: Mode
|
||||
) : LightClassConstructionContext(bindingContext, module, langaugeVersionSettings) {
|
||||
) : LightClassConstructionContext(bindingContext, module, languageVersionSettings) {
|
||||
enum class Mode {
|
||||
LIGHT,
|
||||
EXACT
|
||||
@@ -193,6 +194,8 @@ internal object IDELightClassContexts {
|
||||
|
||||
if (hasSerializationLikeAnnotations(classOrObject)) return false
|
||||
|
||||
if (hasJvmSyntheticMembers(classOrObject)) return false
|
||||
|
||||
return classOrObject.declarations.filterIsInstance<KtClassOrObject>().all { isDummyResolveApplicable(it) }
|
||||
}
|
||||
|
||||
@@ -215,6 +218,12 @@ internal object IDELightClassContexts {
|
||||
private fun isSerializableOrSerializerFqName(fqName: FqName?) =
|
||||
fqName == KOTLINX_SERIALIZABLE_FQ_NAME || fqName == KOTLINX_SERIALIZER_FQ_NAME
|
||||
|
||||
private fun hasJvmSyntheticMembers(classOrObject: KtClassOrObject) =
|
||||
classOrObject.declarations.filterIsInstance<KtFunction>().any { isJvmSynthetic(it) }
|
||||
|
||||
private fun isJvmSynthetic(fn: KtFunction) =
|
||||
fn.annotationEntries.any { it.shortName == JVM_SYNTHETIC_ANNOTATION_FQ_NAME.shortName() }
|
||||
|
||||
private fun hasDelegatedSupertypes(classOrObject: KtClassOrObject) =
|
||||
classOrObject.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry }
|
||||
|
||||
|
||||
+34
-24
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.caches.lightClasses
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiMember
|
||||
@@ -23,7 +24,6 @@ import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
|
||||
import org.jetbrains.kotlin.psi.debugText.getDebugText
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
|
||||
|
||||
typealias ExactLightClassContextProvider = () -> LightClassConstructionContext
|
||||
typealias DummyLightClassContextProvider = (() -> LightClassConstructionContext?)?
|
||||
@@ -112,7 +112,14 @@ sealed class LazyLightClassDataHolder(
|
||||
|
||||
val fieldName = dummyField.name
|
||||
KtLightFieldImpl.lazy(dummyField, fieldOrigin, containingClass) {
|
||||
clsDelegate.findFieldByName(fieldName, false).assertMatches(dummyField, containingClass)
|
||||
val findFieldByName = clsDelegate.findFieldByName(fieldName, false)
|
||||
findFieldByName.checkMatches(dummyField, containingClass) ?:
|
||||
// fallback in case of non-matched (like synthetic) fields
|
||||
//
|
||||
// it costs some performance and has to happen in rare and odd cases
|
||||
KtLightFieldImpl.create(
|
||||
KtLightFieldImpl.getOrigin(dummyField), dummyField, containingClass
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,47 +136,50 @@ sealed class LazyLightClassDataHolder(
|
||||
val byMemberIndex: (PsiMethod) -> Boolean = { it.memberIndex == dummyIndex }
|
||||
|
||||
/* Searching all methods may be necessary in some cases where we failed to rollback optimization:
|
||||
Overriding internal member that was final
|
||||
Overriding internal member that was final
|
||||
|
||||
Resulting light member is not consistent in this case, so this should happen only for erroneous code
|
||||
*/
|
||||
val exactDelegateMethod = clsDelegate.findMethodsByName(dummyMethod.name, false).firstOrNull(byMemberIndex)
|
||||
?: clsDelegate.methods.firstOrNull(byMemberIndex)
|
||||
exactDelegateMethod.assertMatches(dummyMethod, containingClass)
|
||||
val findMethodsByName = clsDelegate.findMethodsByName(dummyMethod.name, false)
|
||||
|
||||
val candidateDelegateMethod = findMethodsByName.firstOrNull(byMemberIndex)
|
||||
?: clsDelegate.methods.firstOrNull(byMemberIndex)
|
||||
|
||||
candidateDelegateMethod.checkMatches(dummyMethod, containingClass) ?:
|
||||
// fallback if unable to find method for a dummy method (e.g. synthetic methods marked explicit or implicit) are
|
||||
// not visible as own methods.
|
||||
//
|
||||
// it costs some performance and has to happen in rare and odd cases
|
||||
KtLightMethodImpl.create(dummyMethod, KtLightMethodImpl.getOrigin(dummyMethod), containingClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : PsiMember> T?.assertMatches(dummyMember: T, containingClass: KtLightClass): T {
|
||||
if (this == null)
|
||||
throw LazyLightClassMemberMatchingError.NoMatch(dummyMember, containingClass)
|
||||
private fun <T : PsiMember> T?.checkMatches(dummyMember: T, containingClass: KtLightClass): T? {
|
||||
if (this == null) {
|
||||
logMismatch("Couldn't match ${dummyMember.debugName}", containingClass)
|
||||
return null
|
||||
}
|
||||
|
||||
val parameterCountMatches = (this as? PsiMethod)?.parameterList?.parametersCount ?: 0 ==
|
||||
(dummyMember as? PsiMethod)?.parameterList?.parametersCount ?: 0
|
||||
if (this.memberIndex != dummyMember.memberIndex || !parameterCountMatches) {
|
||||
throw LazyLightClassMemberMatchingError.WrongMatch(this, dummyMember, containingClass)
|
||||
logMismatch("Wrongly matched ${dummyMember.debugName} to ${this.debugName}", containingClass)
|
||||
return null
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LazyLightClassMemberMatchingError(message: String, containingClass: KtLightClass) :
|
||||
KotlinExceptionWithAttachments(message) {
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(LazyLightClassDataHolder::class.java)
|
||||
|
||||
init {
|
||||
containingClass.kotlinOrigin?.hasLightClassMatchingErrors = true
|
||||
withAttachment("class.kt", (containingClass.kotlinOrigin)?.getDebugText())
|
||||
private fun logMismatch(message: String, containingClass: KtLightClass) {
|
||||
containingClass.kotlinOrigin?.hasLightClassMatchingErrors = true
|
||||
LOG.warn("$message, class.kt: ${(containingClass.kotlinOrigin)?.getDebugText()}")
|
||||
}
|
||||
}
|
||||
|
||||
class NoMatch(dummyMember: PsiMember, containingClass: KtLightClass) : LazyLightClassMemberMatchingError(
|
||||
"Couldn't match ${dummyMember.debugName}", containingClass
|
||||
)
|
||||
|
||||
class WrongMatch(realMember: PsiMember, dummyMember: PsiMember, containingClass: KtLightClass) : LazyLightClassMemberMatchingError(
|
||||
"Matched ${dummyMember.debugName} to ${realMember.debugName}", containingClass
|
||||
)
|
||||
}
|
||||
|
||||
private val PsiMember.debugName
|
||||
|
||||
Reference in New Issue
Block a user