diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.java b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.java index 7d533583aaa..2fbd1f2df14 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.java @@ -204,6 +204,12 @@ public class CliLightClassGenerationSupport extends LightClassGenerationSupport return UtilsPackage.emptyOrSingletonList(KotlinLightClassForPackage.Factory.create(psiManager, packageFqName, scope, filesWithCallables)); } + @Nullable + @Override + public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) { + return bindingContext.get(BindingContext.CLASS, classOrObject); + } + @NotNull @Override public BindingTraceContext createTrace() { diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForExplicitDeclaration.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForExplicitDeclaration.kt index d9a426c4a3a..34c400fd38f 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForExplicitDeclaration.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForExplicitDeclaration.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.idea.JetLanguage import org.jetbrains.kotlin.lexer.JetTokens.* import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -150,8 +151,12 @@ public open class KotlinLightClassForExplicitDeclaration( private fun getJavaFileStub(): PsiJavaFileStub = getLightClassData().javaFileStub protected fun getDescriptor(): ClassDescriptor? { - val data = getLightClassDataExactly(classOrObject) - return data?.descriptor + val cachedValue = getLightClassCachedValue(classOrObject) + if (cachedValue.hasUpToDateValue()) { + return cachedValue.value.dataForClass(classOrObject)?.descriptor + } + + return LightClassGenerationSupport.getInstance(project).resolveClassToDescriptor(classOrObject) } private fun getLightClassData(): OutermostKotlinClassLightClassData { @@ -321,22 +326,17 @@ public open class KotlinLightClassForExplicitDeclaration( override fun isValid(): Boolean = classOrObject.isValid override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean { - // Java inheritor check doesn't work when trait (interface in Java) subclasses Java class and for Kotlin local classes - if (baseClass is KotlinLightClassForExplicitDeclaration || (isInterface && !baseClass.isInterface)) { - val qualifiedName: String? - if (baseClass is KotlinLightClassForExplicitDeclaration) { - val baseDescriptor = baseClass.getDescriptor() - qualifiedName = if (baseDescriptor != null) DescriptorUtils.getFqName(baseDescriptor).asString() else null - } - else { - qualifiedName = baseClass.qualifiedName - } - - val thisDescriptor = getDescriptor() - return qualifiedName != null && thisDescriptor != null && checkSuperTypeByFQName(thisDescriptor, qualifiedName, checkDeep) + val qualifiedName: String? + if (baseClass is KotlinLightClassForExplicitDeclaration) { + val baseDescriptor = baseClass.getDescriptor() + qualifiedName = if (baseDescriptor != null) DescriptorUtils.getFqName(baseDescriptor).asString() else null + } + else { + qualifiedName = baseClass.qualifiedName } - return super.isInheritor(baseClass, checkDeep) + val thisDescriptor = getDescriptor() + return qualifiedName != null && thisDescriptor != null && checkSuperTypeByFQName(thisDescriptor, qualifiedName, checkDeep) } throws(IncorrectOperationException::class) @@ -399,17 +399,23 @@ public open class KotlinLightClassForExplicitDeclaration( } public fun getLightClassData(classOrObject: JetClassOrObject): OutermostKotlinClassLightClassData { + return getLightClassCachedValue(classOrObject).value + } + + public fun getLightClassCachedValue(classOrObject: JetClassOrObject): CachedValue { val outermostClassOrObject = getOutermostClassOrObject(classOrObject) - return CachedValuesManager.getManager(classOrObject.project).getCachedValue( - outermostClassOrObject, - JAVA_API_STUB, - KotlinJavaFileStubProvider.createForDeclaredClass(outermostClassOrObject), - /*trackValue = */false) + var value = outermostClassOrObject.getUserData(JAVA_API_STUB) + if (value == null) { + value = CachedValuesManager.getManager(classOrObject.project).createCachedValue( + KotlinJavaFileStubProvider.createForDeclaredClass(outermostClassOrObject), false) + value = outermostClassOrObject.putUserDataIfAbsent(JAVA_API_STUB, value)!! + } + return value } private fun getLightClassDataExactly(classOrObject: JetClassOrObject): LightClassDataForKotlinClass? { val data = getLightClassData(classOrObject) - return if (data.classOrObject == classOrObject) data else data.allInnerClasses.get(classOrObject) + return data.dataForClass(classOrObject) } private fun getOutermostClassOrObject(classOrObject: JetClassOrObject): JetClassOrObject { @@ -424,11 +430,16 @@ public open class KotlinLightClassForExplicitDeclaration( if (qualifiedName == DescriptorUtils.getFqName(classDescriptor).asString()) return true + val mappedDescriptor = JavaToKotlinClassMap.INSTANCE.mapJavaToKotlin(FqName(qualifiedName)) + val mappedQName = if (mappedDescriptor == null) null else DescriptorUtils.getFqName(mappedDescriptor).asString() + if (qualifiedName == mappedQName) return true + for (superType in classDescriptor.typeConstructor.supertypes) { val superDescriptor = superType.constructor.declarationDescriptor if (superDescriptor is ClassDescriptor) { - if (qualifiedName == DescriptorUtils.getFqName(superDescriptor).asString()) return true + val superQName = DescriptorUtils.getFqName(superDescriptor).asString() + if (superQName == qualifiedName || superQName == mappedQName) return true if (deep) { if (checkSuperTypeByFQName(superDescriptor, qualifiedName, true)) { diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java index 3ab33bc36c2..bb92396fb70 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java @@ -22,6 +22,7 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.JetClassOrObject; import org.jetbrains.kotlin.psi.JetFile; @@ -71,4 +72,7 @@ public abstract class LightClassGenerationSupport { @NotNull public abstract Collection getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope); + + @Nullable + public abstract ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject); } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassStubWithData.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassStubWithData.kt index b011a196ec8..52eebe90bb9 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassStubWithData.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassStubWithData.kt @@ -18,9 +18,9 @@ package org.jetbrains.kotlin.asJava import com.intellij.psi.impl.java.stubs.PsiJavaFileStub import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.JetClassOrObject import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics -import org.jetbrains.kotlin.name.FqName interface LightClassData @@ -53,4 +53,9 @@ data class OutermostKotlinClassLightClassData( override val classOrObject: JetClassOrObject, override val descriptor: ClassDescriptor?, val allInnerClasses: Map -): LightClassDataForKotlinClass, WithFileStubAndExtraDiagnostics +): LightClassDataForKotlinClass, WithFileStubAndExtraDiagnostics { + + fun dataForClass(cls: JetClassOrObject): LightClassDataForKotlinClass? = + if (cls == classOrObject) this else allInnerClasses[cls] + +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.java index 7a96004b973..279e42b47e3 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.java @@ -304,6 +304,17 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport return result; } + @Nullable + @Override + public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) { + try { + return (ClassDescriptor) ResolvePackage.resolveToDescriptor(classOrObject); + } + catch (IllegalStateException e) { + return null; + } + } + @Nullable private static PsiClass getLightClassForDecompiledPackage(@NotNull FqName packageFqName, @NotNull List filesWithCallables) { JetFile firstFile = filesWithCallables.iterator().next(); diff --git a/idea/tests/org/jetbrains/kotlin/asJava/KotlinLightClassInheritorTest.kt b/idea/tests/org/jetbrains/kotlin/asJava/KotlinLightClassInheritorTest.kt new file mode 100644 index 00000000000..5cb110d4226 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/asJava/KotlinLightClassInheritorTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.asJava + +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.testFramework.LightProjectDescriptor +import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.JetLightProjectDescriptor +import org.jetbrains.kotlin.psi.JetClass +import org.jetbrains.kotlin.psi.JetFile +import org.junit.Assert + +public class KotlinLightClassInheritorTest : JetLightCodeInsightFixtureTestCase() { + public fun testAnnotation() { + doTestInheritorByText("annotation class A", "java.lang.annotation.Annotation", false) + } + + public fun testEnum() { + doTestInheritorByText("enum class A", "java.lang.Enum", false) + } + + public fun testIterable() { + doTestInheritorByText("abstract class A : Iterable", "java.lang.Iterable", false) + } + + public fun testIterableDeep() { + doTestInheritorByText("abstract class A : List", "java.lang.Iterable", true) + } + + public fun testObjectDeep() { + doTestInheritorByText("abstract class A : List", "java.lang.Object", true) + } + + private fun doTestInheritorByText(text: String, superQName: String, checkDeep: Boolean) { + val file = myFixture.configureByText("A.kt", text) as JetFile + val jetClass = file.declarations.filterIsInstance().single() + val psiClass = LightClassGenerationSupport.getInstance(project).getPsiClass(jetClass)!! + val baseClass = JavaPsiFacade.getInstance(project).findClass(superQName, GlobalSearchScope.allScope(project))!! + Assert.assertTrue(psiClass.isInheritor(baseClass, checkDeep)) + } + + override fun getProjectDescriptor(): LightProjectDescriptor = JetLightProjectDescriptor.INSTANCE +}