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.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
|
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
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.jvm.platform.JvmPlatformAnalyzerServices
|
||||||
import org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl
|
import org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl
|
||||||
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
|
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
|
||||||
@@ -81,9 +82,9 @@ import org.jetbrains.kotlin.utils.sure
|
|||||||
|
|
||||||
class IDELightClassConstructionContext(
|
class IDELightClassConstructionContext(
|
||||||
bindingContext: BindingContext, module: ModuleDescriptor,
|
bindingContext: BindingContext, module: ModuleDescriptor,
|
||||||
langaugeVersionSettings: LanguageVersionSettings,
|
languageVersionSettings: LanguageVersionSettings,
|
||||||
val mode: Mode
|
val mode: Mode
|
||||||
) : LightClassConstructionContext(bindingContext, module, langaugeVersionSettings) {
|
) : LightClassConstructionContext(bindingContext, module, languageVersionSettings) {
|
||||||
enum class Mode {
|
enum class Mode {
|
||||||
LIGHT,
|
LIGHT,
|
||||||
EXACT
|
EXACT
|
||||||
@@ -193,6 +194,8 @@ internal object IDELightClassContexts {
|
|||||||
|
|
||||||
if (hasSerializationLikeAnnotations(classOrObject)) return false
|
if (hasSerializationLikeAnnotations(classOrObject)) return false
|
||||||
|
|
||||||
|
if (hasJvmSyntheticMembers(classOrObject)) return false
|
||||||
|
|
||||||
return classOrObject.declarations.filterIsInstance<KtClassOrObject>().all { isDummyResolveApplicable(it) }
|
return classOrObject.declarations.filterIsInstance<KtClassOrObject>().all { isDummyResolveApplicable(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +218,12 @@ internal object IDELightClassContexts {
|
|||||||
private fun isSerializableOrSerializerFqName(fqName: FqName?) =
|
private fun isSerializableOrSerializerFqName(fqName: FqName?) =
|
||||||
fqName == KOTLINX_SERIALIZABLE_FQ_NAME || fqName == KOTLINX_SERIALIZER_FQ_NAME
|
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) =
|
private fun hasDelegatedSupertypes(classOrObject: KtClassOrObject) =
|
||||||
classOrObject.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry }
|
classOrObject.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry }
|
||||||
|
|
||||||
|
|||||||
+34
-24
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.caches.lightClasses
|
package org.jetbrains.kotlin.idea.caches.lightClasses
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.psi.PsiClass
|
import com.intellij.psi.PsiClass
|
||||||
import com.intellij.psi.PsiMember
|
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.psi.debugText.getDebugText
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||||
import org.jetbrains.kotlin.storage.StorageManager
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
|
|
||||||
|
|
||||||
typealias ExactLightClassContextProvider = () -> LightClassConstructionContext
|
typealias ExactLightClassContextProvider = () -> LightClassConstructionContext
|
||||||
typealias DummyLightClassContextProvider = (() -> LightClassConstructionContext?)?
|
typealias DummyLightClassContextProvider = (() -> LightClassConstructionContext?)?
|
||||||
@@ -112,7 +112,14 @@ sealed class LazyLightClassDataHolder(
|
|||||||
|
|
||||||
val fieldName = dummyField.name
|
val fieldName = dummyField.name
|
||||||
KtLightFieldImpl.lazy(dummyField, fieldOrigin, containingClass) {
|
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 }
|
val byMemberIndex: (PsiMethod) -> Boolean = { it.memberIndex == dummyIndex }
|
||||||
|
|
||||||
/* Searching all methods may be necessary in some cases where we failed to rollback optimization:
|
/* 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
|
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)
|
val findMethodsByName = clsDelegate.findMethodsByName(dummyMethod.name, false)
|
||||||
?: clsDelegate.methods.firstOrNull(byMemberIndex)
|
|
||||||
exactDelegateMethod.assertMatches(dummyMethod, containingClass)
|
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 {
|
private fun <T : PsiMember> T?.checkMatches(dummyMember: T, containingClass: KtLightClass): T? {
|
||||||
if (this == null)
|
if (this == null) {
|
||||||
throw LazyLightClassMemberMatchingError.NoMatch(dummyMember, containingClass)
|
logMismatch("Couldn't match ${dummyMember.debugName}", containingClass)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
val parameterCountMatches = (this as? PsiMethod)?.parameterList?.parametersCount ?: 0 ==
|
val parameterCountMatches = (this as? PsiMethod)?.parameterList?.parametersCount ?: 0 ==
|
||||||
(dummyMember as? PsiMethod)?.parameterList?.parametersCount ?: 0
|
(dummyMember as? PsiMethod)?.parameterList?.parametersCount ?: 0
|
||||||
if (this.memberIndex != dummyMember.memberIndex || !parameterCountMatches) {
|
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
|
return this
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class LazyLightClassMemberMatchingError(message: String, containingClass: KtLightClass) :
|
companion object {
|
||||||
KotlinExceptionWithAttachments(message) {
|
private val LOG = Logger.getInstance(LazyLightClassDataHolder::class.java)
|
||||||
|
|
||||||
init {
|
private fun logMismatch(message: String, containingClass: KtLightClass) {
|
||||||
containingClass.kotlinOrigin?.hasLightClassMatchingErrors = true
|
containingClass.kotlinOrigin?.hasLightClassMatchingErrors = true
|
||||||
withAttachment("class.kt", (containingClass.kotlinOrigin)?.getDebugText())
|
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
|
private val PsiMember.debugName
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class LightClassEqualsTest extends KotlinLightCodeInsightFixtureTestCase
|
|||||||
doTestEquals(((KtLightClass) theClass).getKotlinOrigin());
|
doTestEquals(((KtLightClass) theClass).getKotlinOrigin());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void doTestEquals(@Nullable KtClassOrObject origin) {
|
static void doTestEquals(@Nullable KtClassOrObject origin) {
|
||||||
assertNotNull(origin);
|
assertNotNull(origin);
|
||||||
|
|
||||||
PsiClass lightClass1 = KtLightClassForSourceDeclaration.Companion.createNoCache(origin, true);
|
PsiClass lightClass1 = KtLightClassForSourceDeclaration.Companion.createNoCache(origin, true);
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.lightClasses
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.registry.Registry
|
||||||
|
import com.intellij.psi.PsiClass
|
||||||
|
import com.intellij.testFramework.LightProjectDescriptor
|
||||||
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
||||||
|
import org.jetbrains.kotlin.idea.lightClasses.LightClassEqualsTest.doTestEquals
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||||
|
class LightClassSampleTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
|
override fun getProjectDescriptor(): LightProjectDescriptor {
|
||||||
|
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testDeprecationLevelHIDDEN() {
|
||||||
|
// KT27243
|
||||||
|
myFixture.configureByText(
|
||||||
|
"a.kt", """
|
||||||
|
interface A {
|
||||||
|
@Deprecated(level = DeprecationLevel.HIDDEN, message = "nothing")
|
||||||
|
fun foo()
|
||||||
|
} """.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
myFixture.configureByText(
|
||||||
|
"b.kt", """
|
||||||
|
class B : A {
|
||||||
|
override fun foo() {}
|
||||||
|
}""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
doTestAndCheck("B", "foo", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testJvmSynthetic() {
|
||||||
|
// KT33561
|
||||||
|
myFixture.configureByText(
|
||||||
|
"foo.kt", """
|
||||||
|
class Foo {
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun foo(crossinline getter: () -> String) = foo(getter())
|
||||||
|
|
||||||
|
fun foo(getter: String) = println(getter)
|
||||||
|
} """.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
doTestAndCheck("Foo", "foo", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTestAndCheck(className: String, methodName: String, methods: Int) {
|
||||||
|
withLightClasses {
|
||||||
|
val theClass: PsiClass = myFixture.javaFacade.findClass(className)
|
||||||
|
assertNotNull(theClass)
|
||||||
|
UsefulTestCase.assertInstanceOf(
|
||||||
|
theClass,
|
||||||
|
KtLightClassForSourceDeclaration::class.java
|
||||||
|
)
|
||||||
|
doTestEquals((theClass as KtLightClass).kotlinOrigin)
|
||||||
|
assertEquals(methods, (theClass.allMethods + theClass.allMethods.flatMap { it.findSuperMethods().toList() })
|
||||||
|
.count { it.name == methodName })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun withLightClasses(block: () -> Any) {
|
||||||
|
val registryValue = Registry.get("kotlin.use.ultra.light.classes")
|
||||||
|
val initialValue = registryValue.asBoolean()
|
||||||
|
registryValue.setValue(false)
|
||||||
|
try {
|
||||||
|
block()
|
||||||
|
} finally {
|
||||||
|
registryValue.setValue(initialValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user