don't perform full resolve and stub building for isInheritor() checks

#KT-8656 Fixed
This commit is contained in:
Dmitry Jemerov
2015-08-19 20:19:06 +02:00
parent 6c32f655f9
commit c00fbb236f
6 changed files with 120 additions and 25 deletions
@@ -204,6 +204,12 @@ public class CliLightClassGenerationSupport extends LightClassGenerationSupport
return UtilsPackage.<PsiClass>emptyOrSingletonList(KotlinLightClassForPackage.Factory.create(psiManager, packageFqName, scope, filesWithCallables)); return UtilsPackage.<PsiClass>emptyOrSingletonList(KotlinLightClassForPackage.Factory.create(psiManager, packageFqName, scope, filesWithCallables));
} }
@Nullable
@Override
public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) {
return bindingContext.get(BindingContext.CLASS, classOrObject);
}
@NotNull @NotNull
@Override @Override
public BindingTraceContext createTrace() { public BindingTraceContext createTrace() {
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.JetLanguage import org.jetbrains.kotlin.idea.JetLanguage
import org.jetbrains.kotlin.lexer.JetTokens.* import org.jetbrains.kotlin.lexer.JetTokens.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -150,8 +151,12 @@ public open class KotlinLightClassForExplicitDeclaration(
private fun getJavaFileStub(): PsiJavaFileStub = getLightClassData().javaFileStub private fun getJavaFileStub(): PsiJavaFileStub = getLightClassData().javaFileStub
protected fun getDescriptor(): ClassDescriptor? { protected fun getDescriptor(): ClassDescriptor? {
val data = getLightClassDataExactly(classOrObject) val cachedValue = getLightClassCachedValue(classOrObject)
return data?.descriptor if (cachedValue.hasUpToDateValue()) {
return cachedValue.value.dataForClass(classOrObject)?.descriptor
}
return LightClassGenerationSupport.getInstance(project).resolveClassToDescriptor(classOrObject)
} }
private fun getLightClassData(): OutermostKotlinClassLightClassData { private fun getLightClassData(): OutermostKotlinClassLightClassData {
@@ -321,22 +326,17 @@ public open class KotlinLightClassForExplicitDeclaration(
override fun isValid(): Boolean = classOrObject.isValid override fun isValid(): Boolean = classOrObject.isValid
override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean { 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 val qualifiedName: String?
if (baseClass is KotlinLightClassForExplicitDeclaration || (isInterface && !baseClass.isInterface)) { if (baseClass is KotlinLightClassForExplicitDeclaration) {
val qualifiedName: String? val baseDescriptor = baseClass.getDescriptor()
if (baseClass is KotlinLightClassForExplicitDeclaration) { qualifiedName = if (baseDescriptor != null) DescriptorUtils.getFqName(baseDescriptor).asString() else null
val baseDescriptor = baseClass.getDescriptor() }
qualifiedName = if (baseDescriptor != null) DescriptorUtils.getFqName(baseDescriptor).asString() else null else {
} qualifiedName = baseClass.qualifiedName
else {
qualifiedName = baseClass.qualifiedName
}
val thisDescriptor = getDescriptor()
return qualifiedName != null && thisDescriptor != null && checkSuperTypeByFQName(thisDescriptor, qualifiedName, checkDeep)
} }
return super.isInheritor(baseClass, checkDeep) val thisDescriptor = getDescriptor()
return qualifiedName != null && thisDescriptor != null && checkSuperTypeByFQName(thisDescriptor, qualifiedName, checkDeep)
} }
throws(IncorrectOperationException::class) throws(IncorrectOperationException::class)
@@ -399,17 +399,23 @@ public open class KotlinLightClassForExplicitDeclaration(
} }
public fun getLightClassData(classOrObject: JetClassOrObject): OutermostKotlinClassLightClassData { public fun getLightClassData(classOrObject: JetClassOrObject): OutermostKotlinClassLightClassData {
return getLightClassCachedValue(classOrObject).value
}
public fun getLightClassCachedValue(classOrObject: JetClassOrObject): CachedValue<OutermostKotlinClassLightClassData> {
val outermostClassOrObject = getOutermostClassOrObject(classOrObject) val outermostClassOrObject = getOutermostClassOrObject(classOrObject)
return CachedValuesManager.getManager(classOrObject.project).getCachedValue( var value = outermostClassOrObject.getUserData(JAVA_API_STUB)
outermostClassOrObject, if (value == null) {
JAVA_API_STUB, value = CachedValuesManager.getManager(classOrObject.project).createCachedValue(
KotlinJavaFileStubProvider.createForDeclaredClass(outermostClassOrObject), KotlinJavaFileStubProvider.createForDeclaredClass(outermostClassOrObject), false)
/*trackValue = */false) value = outermostClassOrObject.putUserDataIfAbsent(JAVA_API_STUB, value)!!
}
return value
} }
private fun getLightClassDataExactly(classOrObject: JetClassOrObject): LightClassDataForKotlinClass? { private fun getLightClassDataExactly(classOrObject: JetClassOrObject): LightClassDataForKotlinClass? {
val data = getLightClassData(classOrObject) 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 { private fun getOutermostClassOrObject(classOrObject: JetClassOrObject): JetClassOrObject {
@@ -424,11 +430,16 @@ public open class KotlinLightClassForExplicitDeclaration(
if (qualifiedName == DescriptorUtils.getFqName(classDescriptor).asString()) return true 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) { for (superType in classDescriptor.typeConstructor.supertypes) {
val superDescriptor = superType.constructor.declarationDescriptor val superDescriptor = superType.constructor.declarationDescriptor
if (superDescriptor is ClassDescriptor) { 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 (deep) {
if (checkSuperTypeByFQName(superDescriptor, qualifiedName, true)) { if (checkSuperTypeByFQName(superDescriptor, qualifiedName, true)) {
@@ -22,6 +22,7 @@ import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.JetClassOrObject; import org.jetbrains.kotlin.psi.JetClassOrObject;
import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.psi.JetFile;
@@ -71,4 +72,7 @@ public abstract class LightClassGenerationSupport {
@NotNull @NotNull
public abstract Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope); public abstract Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope);
@Nullable
public abstract ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject);
} }
@@ -18,9 +18,9 @@ package org.jetbrains.kotlin.asJava
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetClassOrObject import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.name.FqName
interface LightClassData interface LightClassData
@@ -53,4 +53,9 @@ data class OutermostKotlinClassLightClassData(
override val classOrObject: JetClassOrObject, override val classOrObject: JetClassOrObject,
override val descriptor: ClassDescriptor?, override val descriptor: ClassDescriptor?,
val allInnerClasses: Map<JetClassOrObject, InnerKotlinClassLightClassData> val allInnerClasses: Map<JetClassOrObject, InnerKotlinClassLightClassData>
): LightClassDataForKotlinClass, WithFileStubAndExtraDiagnostics ): LightClassDataForKotlinClass, WithFileStubAndExtraDiagnostics {
fun dataForClass(cls: JetClassOrObject): LightClassDataForKotlinClass? =
if (cls == classOrObject) this else allInnerClasses[cls]
}
@@ -304,6 +304,17 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
return result; return result;
} }
@Nullable
@Override
public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) {
try {
return (ClassDescriptor) ResolvePackage.resolveToDescriptor(classOrObject);
}
catch (IllegalStateException e) {
return null;
}
}
@Nullable @Nullable
private static PsiClass getLightClassForDecompiledPackage(@NotNull FqName packageFqName, @NotNull List<JetFile> filesWithCallables) { private static PsiClass getLightClassForDecompiledPackage(@NotNull FqName packageFqName, @NotNull List<JetFile> filesWithCallables) {
JetFile firstFile = filesWithCallables.iterator().next(); JetFile firstFile = filesWithCallables.iterator().next();
@@ -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<T> : Iterable<T>", "java.lang.Iterable", false)
}
public fun testIterableDeep() {
doTestInheritorByText("abstract class A<T> : List<T>", "java.lang.Iterable", true)
}
public fun testObjectDeep() {
doTestInheritorByText("abstract class A<T> : List<T>", "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<JetClass>().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
}